diff --git a/final/dynstr.c b/final/dynstr.c index ba26941..ad1c61f 100644 --- a/final/dynstr.c +++ b/final/dynstr.c @@ -7,36 +7,35 @@ #include #include -typedef struct { +typedef struct String { uint32_t size, capacity; char* data; } String_t; -String_t str_with_capacity(int capacity) { - String_t s; - - /// allocate memory for 'capacity' chars - s.data = (char*)calloc(capacity, sizeof(char)); - - s.size = 0; - s.capacity = capacity; - - return s; +int str_len(String_t* s) { + return (int)s->size; } -String_t str_from_chars(const char* string) { - String_t s; +char* to_chars(String_t* s) { + // allocate buffer with null terminator + char* buffer = (char*)calloc(s->size + 1, sizeof(char)); + for (int i = 0; i < s->size; i++) { + buffer[i] = s->data[i]; + } + buffer[s->size] = '\0'; + return buffer; +} - s.data = (char*)calloc(strlen(string), sizeof(char)); - strcpy(s.data, string); - s.size = strlen(string); - s.capacity = strlen(string); +String_t str_with_capacity(int capacity) { + String_t s; + s.data = (char*)calloc(capacity, sizeof(char)); + s.size = 0; + s.capacity = capacity; return s; } String_t str_from_slice(const char* string, int len) { String_t s; - s.data = (char*)calloc(len, sizeof(char)); strncpy(s.data, string, len); s.size = len; @@ -44,27 +43,26 @@ String_t str_from_slice(const char* string, int len) { return s; } +String_t str_from_chars(const char* string) { + return str_from_slice(string, strlen(string)); +} String_t str_new() { return str_with_capacity(1); } - - int str_dealloc(String_t* self) { free(self->data); return 0; } int str_push(String_t* self, char c) { - // check size < capacity if (self->size < self->capacity) { self->data[self->size] = c; self->size++; return 0; } - // reallocate to add capacity for an extra char int newcap = self->capacity + sizeof(char); self->data = (char*)realloc(self->data, newcap); self->data[self->size] = c; @@ -75,41 +73,57 @@ int str_push(String_t* self, char c) { int str_push_str(String_t* self, char* c) { if (self->size + strlen(c) >= self->capacity) { - self->capacity = ( self->size + strlen(c) ) * sizeof(char); + self->capacity = (self->size + strlen(c)) * sizeof(char); self->data = (char*)realloc(self->data, self->capacity); - - for (int i = 0; i < strlen(c); i++) { - self->data[self->size + i] = c[i]; - } - self->size = self->capacity; } + for (int i = 0; i < strlen(c); i++) { + self->data[self->size + i] = c[i]; + } + self->size = self->capacity; + return 0; } String_t str_from_file(FILE* file) { if (file == NULL) { return str_new(); } - char buffer[64]; - String_t string = str_new(); - while (fgets(buffer, sizeof(buffer), file) != NULL) { str_push_str(&string, buffer); } - fclose(file); - return string; } + +int str_to_file(String_t* self, FILE* file) { + char* data = to_chars(self); + + if (fprintf(file, "%s\n", data) == -1) { + free(data); + return -1; + } + free(data); + return 0; +} + + +String_t str_clone(String_t* self) { + String_t clone; + clone.size = self->size; + clone.capacity = self->capacity; + clone.data = (char*)malloc(clone.capacity); + memcpy(clone.data, self->data, clone.capacity); + return clone; +} + char str_pop(String_t* self) { if (self->size == 0) { return '\0'; } self->size--; - char c = self->data[self->size]; - return c; + return self->data[self->size]; } int str_insert(String_t* self, int index, char c) { @@ -117,6 +131,10 @@ int str_insert(String_t* self, int index, char c) { return -1; } self->size++; + if (self->size > self->capacity) { + self->capacity = (self->size) * sizeof(char); + self->data = (char*)realloc(self->data, self->capacity); + } for (int i = self->size - 1; i > index; i--) { self->data[i] = self->data[i - 1]; } @@ -135,22 +153,16 @@ int str_remove(String_t* self, int index) { return 0; } -/// splits a string into an array of strings based on a delimiter String_t* str_split(String_t* self, int* res_len, char c) { - char* string = self->data; - String_t* elements = NULL; - - // find the number of lines in the file - bool flag = false; *res_len = 0; - for (int i = 0; i < strlen(string); i++) { + + // Count number of splits + for (int i = 0; i < self->size; i++) { if (string[i] == c) { - // if (flag) { // this condition was here so that empty strings would be ignore (*res_len)++; flag = false; - // } } else { flag = true; } @@ -159,20 +171,17 @@ String_t* str_split(String_t* self, int* res_len, char c) { (*res_len)++; } - // allocate memory for an array of pointers to each line - elements = (String_t*)malloc((*res_len) * sizeof(String_t)); - + String_t* elements = (String_t*)malloc((*res_len) * sizeof(String_t)); int i = 0; flag = false; char* start = string; char* end = string; + char* string_end = string + self->size; - while (*end != '\0') { + while (end < string_end) { if (*end == c) { - // if (flag) { elements[i] = str_from_slice(start, end - start); i++; - // } end++; start = end; flag = false; @@ -180,12 +189,10 @@ String_t* str_split(String_t* self, int* res_len, char c) { end++; flag = true; } - } if (flag) { + } + if (flag) { elements[i] = str_from_slice(start, end - start); } - - - // returns an array of String_t return elements; } @@ -209,38 +216,32 @@ String_t* str_lines(String_t* self, int* numlines) { String_t str_slice(String_t* s, int start, int end) { String_t s2; + + if (start < 0) start = 0; + if (end > s->size) end = s->size; + + // if the range is invalid, we return an empty string + if (start >= end || start >= s->size) { + s2.size = 0; + s2.capacity = 1; + s2.data = (char*)calloc(1, sizeof(char)); + return s2; + } + s2.size = end - start; s2.capacity = s2.size; + s2.data = (char*)calloc(s2.size, sizeof(char)); + + // if the allocation fails we return a zero sized string + if (s2.data == NULL) { + s2.size = 0; + s2.capacity = 0; + return s2; + } + for (int i = 0; i < s2.size; i++) { s2.data[i] = s->data[start + i]; } return s2; } - -int str_len(String_t* s) { - return s->size; -} - -char* to_chars(String_t* s) { - return s->data; -} - -// int main() { -// String_t s = str_from_chars("hello\nworld\neeeee\notherline\n"); - -// str_remove(&s, 10); -// str_insert(&s, 10, 'h'); - -// printf("%s\n", to_chars(&s)); - -// int linen = 0; -// String_t* lines = str_lines(&s, &linen); -// for (int i=0; i < linen; i++) { -// printf("%s", to_chars(&lines[i])); -// } - -// str_dealloc(&s); - -// return 0; -// } diff --git a/final/dynstr.h b/final/dynstr.h index 9734df5..ccd4e41 100644 --- a/final/dynstr.h +++ b/final/dynstr.h @@ -2,44 +2,191 @@ #define DYNSTR_H #include -typedef struct { - int size; - int capacity; +/** + * Custom String type that stores an array of characters +*/ +typedef struct String { + uint32_t size, capacity; char* data; } String_t; +/** + * Creates a new string with the specified initial capacity. + * The string will be empty but can hold up to capacity characters + * without requiring reallocation. + * + * @param capacity Initial capacity in bytes + * @return A new String_t structure with allocated memory + */ String_t str_with_capacity(int capacity); +/** + * Creates a new string from a null-terminated character array. + * + * @param string Null-terminated source string + * @return A new String_t containing a copy of the input string + */ String_t str_from_chars(char* string); +/** + * Creates a new string from a character array slice. + * Allocates new memory and copies the specified number of characters. + * + * @param string Source character array + * @param len Number of characters to copy + * @return A new String_t containing the copied characters + */ String_t str_from_slice(char* string, int len); +/** + * Merges two strings into a new string. + * The resulting string contains s1's contents followed by s2's contents. + * + * @param s1 Pointer to the first String_t + * @param s2 Pointer to the second String_t + * @return A new String_t containing the merged contents + */ String_t str_merge(String_t* s1, String_t* s2); +/** + * Creates a new string by reading from a file. + * Reads the file line by line and concatenates all lines. + * + * @param file Pointer to an open FILE + * @return A new String_t containing the file's contents + */ String_t str_from_file(FILE* file); -int str_push_str(String_t* string, char* other); - +/** + * Creates a new empty string with minimal capacity. + * + * @return A new empty String_t with capacity of 1 byte + */ String_t str_new(); -String_t* str_lines(String_t* self, int* numlines); +/** + * Returns a clone of the string. + * This allocates new memory that must be freed + * @param self Pointer to the String_t to clone +*/ +String_t str_clone(); +/** + * Creates a new string containing a slice of an existing string. + * The slice includes characters from start index up to (but not including) end index. + * + * @param s Pointer to the source String_t + * @param start Starting index (inclusive) + * @param end Ending index (exclusive) + * @return A new String_t containing the specified slice + */ String_t str_slice(String_t* s, int start, int end); -// String_t* str_split(String_t* self, int* res_len, char c); +/** + * Splits a string into lines based on newline characters. + * Convenience wrapper around str_split that uses '\n' as delimiter. + * + * @param self Pointer to the String_t to split + * @param numlines Pointer to store the number of resulting lines + * @return Array of String_t structures, one per line + */ +String_t* str_lines(String_t* self, int* numlines); -void str_push(String_t* s, char c); +/** + * Splits a string into an array of strings based on a delimiter. + * Each occurrence of the delimiter creates a new string. + * + * @param self Pointer to the String_t to split + * @param res_len Pointer to store the number of resulting strings + * @param c Delimiter character + * @return Array of String_t structures, must be freed by caller + */ +String_t* str_split(String_t* self, int* res_len, char c); +/** + * Removes and returns the last character in the string. + * + * @param self Pointer to the String_t to modify + * @return The removed character, or '\0' if string is empty + */ char str_pop(String_t* s); -void str_insert(String_t* s, int index, char c); - -void str_remove(String_t* s, int index); - -int str_dealloc(String_t* s); - +/** + * Returns a pointer to the underlying character array. + * Note: The returned pointer is still owned by the String_t structure. + * + * @param s Pointer to the String_t structure + * @return Pointer to the null-terminated character array + */ char* to_chars(String_t* s); +/** + * Writes the string's contents to a file. + * Appends a newline character after writing the string. + * + * @param self Pointer to the String_t to write + * @param file Pointer to an open FILE + * @return 0 on success, -1 on failure + */ +int str_to_file(String_t* self, FILE* file); + +/** + * Appends a null-terminated string to the end of the string. + * Automatically reallocates if more capacity is needed. + * + * @param self Pointer to the String_t to modify + * @param c Null-terminated string to append + * @return 0 on success + */ +int str_push_str(String_t* string, char* other); + +/** + * Deallocates the memory used by a string. + * Must be called when the string is no longer needed to prevent memory leaks. + * + * @param self Pointer to the String_t to deallocate + * @return 0 on success + */ +int str_dealloc(String_t* s); + +/** + * Returns the current length of the string. + * + * @param s Pointer to the String_t structure + * @return The number of characters in the string + */ int str_len(String_t* s); +/** + * Inserts a character at the specified index. + * Shifts existing characters right to make room. + * Automatically reallocates if more capacity is needed. + * + * @param self Pointer to the String_t to modify + * @param index Position to insert the character + * @param c Character to insert + * @return 0 on success, -1 if index is out of bounds + */ +void str_insert(String_t* s, int index, char c); + +/** + * Removes the character at the specified index. + * Shifts remaining characters left to fill the gap. + * + * @param self Pointer to the String_t to modify + * @param index Position of character to remove + * @return 0 on success, -1 if index is out of bounds + */ +void str_remove(String_t* s, int index); + +/** + * Appends a single character to the end of the string. + * Automatically reallocates if more capacity is needed. + * + * @param self Pointer to the String_t to modify + * @param c Character to append + * @return 0 on success + */ +void str_push(String_t* s, char c); + #endif diff --git a/final/editor.c b/final/editor.c index 56b321d..4e02d74 100644 --- a/final/editor.c +++ b/final/editor.c @@ -3,8 +3,12 @@ #include "dynstr.h" #include +#define min(x, y) ((x) < (y) ? (x) : (y)) +#define max(x, y) ((x) > (y) ? (x) : (y)) + typedef struct { uint32_t lines; + uint32_t capacity; uint32_t buffer_line; uint32_t buffer_col; uint32_t y_offset; @@ -12,6 +16,8 @@ typedef struct { bool unsaved_changes; bool editmode; String_t* buffer; + String_t* original; + String_t filename; } Editor; void add_toolbar(Editor* self) { @@ -22,7 +28,7 @@ void add_toolbar(Editor* self) { // add an entry for the current mode move(max_y - 1, 0); - char mode[8]; + char mode[9]; snprintf(mode, 9, "[%6s]", self->editmode ? "Insert" : "Normal"); addstr(mode); @@ -40,80 +46,94 @@ void add_toolbar(Editor* self) { attroff(COLOR_PAIR(1)); } -#define min(x, y) ((x) < (y) ? (x) : (y)) void refresh_buffer(Editor* self) { - move(0, 0); - clear(); // get the screen size so we can figure out where to place characters int max_x, max_y; getmaxyx(stdscr, max_y, max_x); + max_y--; // Reserve bottom line for toolbar + // Ensure y_offset is valid + if (self->buffer_line >= self->y_offset + max_y) { + self->y_offset = self->buffer_line - max_y + 1; + } + if (self->buffer_line < self->y_offset) { + self->y_offset = self->buffer_line; + } + + // Display visible lines for (size_t i = self->y_offset; i < self->lines && i < self->y_offset + max_y; i++) { + move(i - self->y_offset, 0); + // adding the line number - attron(COLOR_PAIR(1)); char line_no[6]; - snprintf(line_no, 6, "%-6d", i + 1); + snprintf(line_no, 6, "%-4d]", i + 1); addstr(line_no); attroff(COLOR_PAIR(1)); - // refreshing the text for the line - String_t line_segment = str_slice( - &self->buffer[i], - self->x_offset, - min(str_len(&self->buffer[i]), self->x_offset + max_x - 5) - ); + // Only try to display the line if it's within bounds + if (i < self->lines) { + // Calculate visible portion of line + int visible_start = self->x_offset; + int line_len = str_len(&self->buffer[i]); + int visible_end = min(line_len, self->x_offset + max_x - 5); + + if (visible_start < line_len) { + String_t line_segment = str_slice( + &self->buffer[i], + visible_start, + visible_end + ); - addstr(to_chars(&line_segment)); - str_dealloc(&line_segment); - - addstr("\n"); + char* data = to_chars(&line_segment); + + addstr(data); + str_dealloc(&line_segment); + free(data); + } + } } add_toolbar(self); - move(self->buffer_line - self->y_offset, self->buffer_col + 5 - self->x_offset); + // Ensure cursor position is valid + int cursor_y = self->buffer_line - self->y_offset; + int cursor_x = self->buffer_col - self->x_offset + 5; + + // Clamp cursor position to screen bounds + cursor_y = min(max(cursor_y, 0), max_y - 1); + cursor_x = min(max(cursor_x, 5), max_x - 1); + + move(cursor_y, cursor_x); } void move_cursor_on_screen(Editor* editor, int x, int y) { - add_toolbar(editor); - - int newx = x + 5; // account for the line number - int max_x, max_y; getmaxyx(stdscr, max_y, max_x); + max_y--; // Reserve bottom line for toolbar + max_x -= 5; // Account for line number margin - bool flag = false; - - while (newx + 3 > max_x + editor->x_offset) { - editor->x_offset += 1; - flag = true; + // Adjust x_offset if cursor would be off screen + if (x + 5 >= max_x + editor->x_offset) { + editor->x_offset = x - max_x + 6; + } + if (x < editor->x_offset) { + editor->x_offset = x; } - while (newx - 3 < editor->x_offset) { - editor->x_offset -= 1; - flag = true; + // Adjust y_offset if cursor would be off screen + if (y >= max_y + editor->y_offset) { + editor->y_offset = y - max_y + 1; + } + if (y < editor->y_offset) { + editor->y_offset = y; } - while (y + 3 > max_y + editor->y_offset) { - editor->y_offset += 1; - flag = true; - } - - while (y - 3 < editor->y_offset) { - editor->y_offset -= 1; - flag = true; - } - - if (flag) { - refresh_buffer(editor); - } - - move(y - editor->y_offset, newx - editor->x_offset); + refresh_buffer(editor); } void switch_mode(Editor* self) { @@ -125,7 +145,6 @@ void newline(Editor* self) { move(self->buffer_line, 0); char line_no[6]; snprintf(line_no, 6, "%-6d", self->buffer_line + 1); - fprintf(stderr, "ln: %d, col: %d", self->buffer_line, self->buffer_col); // add the line number attron(COLOR_PAIR(1)); @@ -134,7 +153,7 @@ void newline(Editor* self) { move_cursor_on_screen(self, self->buffer_col, self->buffer_line); } -Editor editor_from(String_t input_string) { +Editor editor_from(String_t input_string, String_t filename) { Editor e; e.lines = 0; e.buffer_line = 0; @@ -143,24 +162,46 @@ Editor editor_from(String_t input_string) { e.x_offset = 0; e.editmode = false; e.unsaved_changes = false; + e.filename = filename; int linenum = 0; e.buffer = str_lines(&input_string, &linenum); - e.lines = (size_t)linenum; + e.lines = (uint32_t)linenum; + e.capacity = e.lines; + str_dealloc(&input_string); refresh_buffer(&e); return e; } -Editor new_editor() { - return editor_from(str_new()); +Editor new_editor(String_t filename) { + return editor_from(str_new(), filename); +} + +int save_file(Editor* self) { + char* name = to_chars(&self->filename); + + // writes the content line by line to the file + FILE* file = fopen(name, "w"); + for (size_t i = 0; i < self->lines; i++) { + if (str_to_file(&self->buffer[i], file) == -1) { + + free(name); + fprintf(stderr, "Error writing to file\n"); + return -1; + } + } + + free(name); + fclose(file); + return 0; } void move_cursor(Editor* self, int x, int y) { if (y != 0 && (int)(self->buffer_line) + y >= 0 - && self->buffer_line + y <= self->lines) + && self->buffer_line + y < self->lines) { // move the cursor up or down self->buffer_line += y; @@ -174,17 +215,19 @@ void move_cursor(Editor* self, int x, int y) { self->buffer_line -= 1; self->buffer_col = str_len(&self->buffer[self->buffer_line]); } - } else if (self->buffer_col + x > str_len(&self->buffer[self->buffer_line])) { - // moving the cursor off the right hand side of the line - if (self->buffer_line < self->lines) { - self->buffer_col = 0; - self->buffer_line += 1; + } else if (x != 0 && self->buffer_line < self->lines) { + int line_width = str_len(&self->buffer[self->buffer_line]); + if (self->buffer_col + x > line_width) { + // moving the cursor off the right hand side of the line + if (self->buffer_line + 1 < self->lines) { + self->buffer_col = 0; + self->buffer_line += 1; + } + } else { + // moving the cursor left or right in any other case + self->buffer_col += x; } - } else if (x != 0) { - // moving the cursor left or right in any other case - self->buffer_col += x; } - fprintf(stderr, "ln: %d, col: %d xoff: %d yoff: %d\n", self->buffer_line, self->buffer_col, self->x_offset, self->y_offset); move_cursor_on_screen(self, self->buffer_col, self->buffer_line); } @@ -197,12 +240,22 @@ void delchar(Editor* self) { return; } + // obtain string objects that reference the old lines. + String_t old_curr_line = self->buffer[self->buffer_line]; + String_t old_next_line = self->buffer[self->buffer_line + 1]; + + // assign the new line self->buffer[self->buffer_line] = str_merge(&self->buffer[self->buffer_line], &self->buffer[self->buffer_line + 1]); + // deallocate the memory for the old lines + str_dealloc(&old_curr_line); + str_dealloc(&old_next_line); + for (size_t i = self->buffer_line + 2; i < self->lines; i++) { self->buffer[i - 1] = self->buffer[i]; } + // replace the last line self->buffer[self->lines - 1] = str_new(); self->lines--; refresh_buffer(self); @@ -219,7 +272,11 @@ void pressed_enter(Editor* self) { // allocate memory immediately since we know a new line is being added self->lines++; - self->buffer = realloc(self->buffer, sizeof(String_t) * (self->lines)); + + while (self->lines > self->capacity) { + self->capacity++; + } + self->buffer = (String_t*)realloc(self->buffer, sizeof(String_t) * (self->capacity)); if (self->buffer_line == self->lines -1) { // if at end of file add an empty line @@ -230,6 +287,8 @@ void pressed_enter(Editor* self) { self->buffer[i] = self->buffer[i - 1]; } + String_t old_line = self->buffer[self->buffer_line]; + self->buffer[self->buffer_line + 1] = str_slice( &self->buffer[self->buffer_line], self->buffer_col, @@ -241,6 +300,8 @@ void pressed_enter(Editor* self) { 0, self->buffer_col ); + + str_dealloc(&old_line); } // refresh the screen with the new data refresh_buffer(self); @@ -257,7 +318,12 @@ void addchar(Editor* self, char c) { // reallocate self->buffer to be 1 larger self->lines++; - self->buffer = realloc(self->buffer, sizeof(String_t) * (self->lines)); + + while (self->lines > self->capacity) { + self->capacity++; + } + + self->buffer = (String_t*)realloc(self->buffer, sizeof(String_t) * (self->capacity)); // allocate the memory space for the new line and add it to the buffer self->buffer[self->buffer_line] = str_new(); @@ -269,7 +335,14 @@ void addchar(Editor* self, char c) { move_cursor(self, 1, 0); } +void shutdown_editor(Editor* self) { + for (size_t i = 0; i < self->capacity; i++) { + str_dealloc(&self->buffer[i]); + } + str_dealloc(&self->filename); + free(self->buffer); +} + String_t* to_string(Editor* self) { } - diff --git a/final/editor.h b/final/editor.h index 66e3cda..24f1b6b 100644 --- a/final/editor.h +++ b/final/editor.h @@ -1,8 +1,13 @@ #include "dynstr.h" #include +/** + * maintains the state of a text editor, including the text buffer, + * cursor position, and various editor settings. + */ typedef struct { uint32_t lines; + uint32_t capacity; uint32_t buffer_line; uint32_t buffer_col; uint32_t y_offset; @@ -10,16 +15,84 @@ typedef struct { bool unsaved_changes; bool editmode; String_t* buffer; + String_t* original; + String_t filename; } Editor; -Editor new_editor(); +/** + * Creates a new editor instance from a filename + * + * @param filename The name of the file to open + * @return Editor A new editor instance initialized with the file's content + */ +Editor new_editor(String_t filename); -Editor editor_from(String_t input_string); +/** + * Creates a new editor instance from an input string + * + * @param input_string initial editor content + * @param filename name of the file being edited + * @return an instance of the editor + */ +Editor editor_from(String_t input_string, String_t filename); +/** + * Moves the cursor in the editor (relative to it's current position) + * + * @param self Pointer to the editor instance + * @param x Horizontal movement (negative for left, positive for right) + * @param y Vertical movement (negative for up, positive for down) + */ void move_cursor(Editor* self, int x, int y); + +/** + * Deletes the character before the cursor + * + * @param self Pointer to the editor instance + */ void delchar(Editor* self); + +/** + * Adds a character at the cursor position + * + * @param self Pointer to the editor instance + * @param c The character to add + */ void addchar(Editor* self, char c); + +/** + * Handles the enter key press, creating a new line + * + * @param self Pointer to the editor instance + */ void pressed_enter(Editor* self); + +/** + * Toggles between edit and command mode + * + * @param self Pointer to the editor instance + */ void switch_mode(Editor* self); +/** + * Cleans up and frees resources used by the editor + * + * @param self Pointer to the editor instance + */ +void shutdown_editor(Editor* self); + +/** + * Saves the current buffer content to the file + * + * @param self Pointer to the editor instance + * @return int Returns 0 on success, non-zero on failure + */ +int save_file(Editor* self); + +/** + * Converts the editor's buffer content to a String_t type + * + * @param self Pointer to the editor instance + * @return String_t* Pointer to a string containing the buffer content + */ String_t* to_string(Editor* self); diff --git a/final/log.txt b/final/log.txt deleted file mode 100644 index b0c1c15..0000000 --- a/final/log.txt +++ /dev/null @@ -1,630 +0,0 @@ -starting editor openiing file somecode.cln: 1, col: 0 xoff: 0 yoff: 0 -ln: 2, col: 0 xoff: 0 yoff: 0 -ln: 3, col: 0 xoff: 0 yoff: 0 -ln: 4, col: 0 xoff: 0 yoff: 0 -ln: 5, col: 0 xoff: 0 yoff: 0 -ln: 6, col: 0 xoff: 0 yoff: 0 -ln: 7, col: 0 xoff: 0 yoff: 0 -ln: 8, col: 0 xoff: 0 yoff: 0 -ln: 9, col: 0 xoff: 0 yoff: 0 -ln: 10, col: 0 xoff: 0 yoff: 0 -ln: 11, col: 0 xoff: 0 yoff: 0 -ln: 12, col: 0 xoff: 0 yoff: 0 -ln: 13, col: 0 xoff: 0 yoff: 0 -ln: 14, col: 0 xoff: 0 yoff: 0 -ln: 15, col: 0 xoff: 0 yoff: 0 -ln: 16, col: 0 xoff: 0 yoff: 0 -ln: 17, col: 0 xoff: 0 yoff: 0 -ln: 18, col: 0 xoff: 0 yoff: 0 -ln: 19, col: 0 xoff: 0 yoff: 0 -ln: 20, col: 0 xoff: 0 yoff: 0 -ln: 21, col: 0 xoff: 0 yoff: 0 -ln: 22, col: 0 xoff: 0 yoff: 0 -ln: 23, col: 0 xoff: 0 yoff: 0 -ln: 24, col: 0 xoff: 0 yoff: 0 -ln: 25, col: 0 xoff: 0 yoff: 0 -ln: 26, col: 0 xoff: 0 yoff: 0 -ln: 27, col: 0 xoff: 0 yoff: 0 -ln: 28, col: 0 xoff: 0 yoff: 0 -ln: 29, col: 0 xoff: 0 yoff: 0 -ln: 30, col: 0 xoff: 0 yoff: 0 -ln: 31, col: 0 xoff: 0 yoff: 0 -ln: 32, col: 0 xoff: 0 yoff: 0 -ln: 33, col: 0 xoff: 0 yoff: 0 -ln: 34, col: 0 xoff: 0 yoff: 0 -ln: 35, col: 0 xoff: 0 yoff: 0 -ln: 36, col: 0 xoff: 0 yoff: 0 -ln: 37, col: 0 xoff: 0 yoff: 0 -ln: 38, col: 0 xoff: 0 yoff: 0 -ln: 39, col: 0 xoff: 0 yoff: 0 -ln: 40, col: 0 xoff: 0 yoff: 0 -ln: 41, col: 0 xoff: 0 yoff: 0 -ln: 42, col: 0 xoff: 0 yoff: 0 -ln: 43, col: 0 xoff: 0 yoff: 0 -ln: 44, col: 0 xoff: 0 yoff: 0 -ln: 45, col: 0 xoff: 0 yoff: 0 -ln: 46, col: 0 xoff: 0 yoff: 0 -ln: 47, col: 0 xoff: 0 yoff: 0 -ln: 48, col: 0 xoff: 0 yoff: 0 -ln: 49, col: 0 xoff: 0 yoff: 0 -ln: 50, col: 0 xoff: 0 yoff: 0 -ln: 51, col: 0 xoff: 0 yoff: 0 -ln: 52, col: 0 xoff: 0 yoff: 0 -ln: 53, col: 0 xoff: 0 yoff: 0 -ln: 54, col: 0 xoff: 0 yoff: 0 -ln: 55, col: 0 xoff: 0 yoff: 0 -ln: 56, col: 0 xoff: 0 yoff: 0 -ln: 57, col: 0 xoff: 0 yoff: 0 -ln: 58, col: 0 xoff: 0 yoff: 0 -ln: 59, col: 0 xoff: 0 yoff: 0 -ln: 60, col: 0 xoff: 0 yoff: 0 -ln: 61, col: 0 xoff: 0 yoff: 0 -ln: 62, col: 0 xoff: 0 yoff: 0 -ln: 63, col: 0 xoff: 0 yoff: 1 -ln: 64, col: 0 xoff: 0 yoff: 2 -ln: 65, col: 0 xoff: 0 yoff: 3 -ln: 66, col: 0 xoff: 0 yoff: 4 -ln: 67, col: 0 xoff: 0 yoff: 5 -ln: 68, col: 0 xoff: 0 yoff: 6 -ln: 69, col: 0 xoff: 0 yoff: 7 -ln: 70, col: 0 xoff: 0 yoff: 8 -ln: 71, col: 0 xoff: 0 yoff: 9 -ln: 72, col: 0 xoff: 0 yoff: 10 -ln: 73, col: 0 xoff: 0 yoff: 11 -ln: 74, col: 0 xoff: 0 yoff: 12 -ln: 75, col: 0 xoff: 0 yoff: 13 -ln: 76, col: 0 xoff: 0 yoff: 14 -ln: 77, col: 0 xoff: 0 yoff: 15 -ln: 78, col: 0 xoff: 0 yoff: 16 -ln: 79, col: 0 xoff: 0 yoff: 17 -ln: 80, col: 0 xoff: 0 yoff: 18 -ln: 81, col: 0 xoff: 0 yoff: 19 -ln: 82, col: 0 xoff: 0 yoff: 20 -ln: 83, col: 0 xoff: 0 yoff: 21 -ln: 84, col: 0 xoff: 0 yoff: 22 -ln: 85, col: 0 xoff: 0 yoff: 23 -ln: 86, col: 0 xoff: 0 yoff: 24 -ln: 87, col: 0 xoff: 0 yoff: 25 -ln: 88, col: 0 xoff: 0 yoff: 26 -ln: 89, col: 0 xoff: 0 yoff: 27 -ln: 90, col: 0 xoff: 0 yoff: 28 -ln: 91, col: 0 xoff: 0 yoff: 29 -ln: 92, col: 0 xoff: 0 yoff: 30 -ln: 93, col: 0 xoff: 0 yoff: 31 -ln: 94, col: 0 xoff: 0 yoff: 32 -ln: 95, col: 0 xoff: 0 yoff: 33 -ln: 96, col: 0 xoff: 0 yoff: 34 -ln: 97, col: 0 xoff: 0 yoff: 35 -ln: 98, col: 0 xoff: 0 yoff: 36 -ln: 99, col: 0 xoff: 0 yoff: 37 -ln: 100, col: 0 xoff: 0 yoff: 38 -ln: 101, col: 0 xoff: 0 yoff: 39 -ln: 102, col: 0 xoff: 0 yoff: 40 -ln: 103, col: 0 xoff: 0 yoff: 41 -ln: 104, col: 0 xoff: 0 yoff: 42 -ln: 105, col: 0 xoff: 0 yoff: 43 -ln: 106, col: 0 xoff: 0 yoff: 44 -ln: 107, col: 0 xoff: 0 yoff: 45 -ln: 108, col: 0 xoff: 0 yoff: 46 -ln: 109, col: 0 xoff: 0 yoff: 47 -ln: 110, col: 0 xoff: 0 yoff: 48 -ln: 111, col: 0 xoff: 0 yoff: 49 -ln: 112, col: 0 xoff: 0 yoff: 50 -ln: 113, col: 0 xoff: 0 yoff: 51 -ln: 114, col: 0 xoff: 0 yoff: 52 -ln: 115, col: 0 xoff: 0 yoff: 53 -ln: 116, col: 0 xoff: 0 yoff: 54 -ln: 117, col: 0 xoff: 0 yoff: 55 -ln: 118, col: 0 xoff: 0 yoff: 56 -ln: 119, col: 0 xoff: 0 yoff: 57 -ln: 120, col: 0 xoff: 0 yoff: 58 -ln: 121, col: 0 xoff: 0 yoff: 59 -ln: 122, col: 0 xoff: 0 yoff: 60 -ln: 123, col: 0 xoff: 0 yoff: 61 -ln: 124, col: 0 xoff: 0 yoff: 62 -ln: 125, col: 0 xoff: 0 yoff: 63 -ln: 126, col: 0 xoff: 0 yoff: 64 -ln: 127, col: 0 xoff: 0 yoff: 65 -ln: 128, col: 0 xoff: 0 yoff: 66 -ln: 129, col: 0 xoff: 0 yoff: 67 -ln: 130, col: 0 xoff: 0 yoff: 68 -ln: 131, col: 0 xoff: 0 yoff: 69 -ln: 132, col: 0 xoff: 0 yoff: 70 -ln: 133, col: 0 xoff: 0 yoff: 71 -ln: 134, col: 0 xoff: 0 yoff: 72 -ln: 135, col: 0 xoff: 0 yoff: 73 -ln: 136, col: 0 xoff: 0 yoff: 74 -ln: 137, col: 0 xoff: 0 yoff: 75 -ln: 138, col: 0 xoff: 0 yoff: 76 -ln: 139, col: 0 xoff: 0 yoff: 77 -ln: 140, col: 0 xoff: 0 yoff: 78 -ln: 141, col: 0 xoff: 0 yoff: 79 -ln: 142, col: 0 xoff: 0 yoff: 80 -ln: 143, col: 0 xoff: 0 yoff: 81 -ln: 144, col: 0 xoff: 0 yoff: 82 -ln: 145, col: 0 xoff: 0 yoff: 83 -ln: 146, col: 0 xoff: 0 yoff: 84 -ln: 147, col: 0 xoff: 0 yoff: 85 -ln: 148, col: 0 xoff: 0 yoff: 86 -ln: 149, col: 0 xoff: 0 yoff: 87 -ln: 150, col: 0 xoff: 0 yoff: 88 -ln: 151, col: 0 xoff: 0 yoff: 89 -ln: 152, col: 0 xoff: 0 yoff: 90 -ln: 153, col: 0 xoff: 0 yoff: 91 -ln: 154, col: 0 xoff: 0 yoff: 92 -ln: 155, col: 0 xoff: 0 yoff: 93 -ln: 156, col: 0 xoff: 0 yoff: 94 -ln: 157, col: 0 xoff: 0 yoff: 95 -ln: 158, col: 0 xoff: 0 yoff: 96 -ln: 159, col: 0 xoff: 0 yoff: 97 -ln: 160, col: 0 xoff: 0 yoff: 98 -ln: 161, col: 0 xoff: 0 yoff: 99 -ln: 162, col: 0 xoff: 0 yoff: 100 -ln: 163, col: 0 xoff: 0 yoff: 101 -ln: 164, col: 0 xoff: 0 yoff: 102 -ln: 165, col: 0 xoff: 0 yoff: 103 -ln: 164, col: 0 xoff: 0 yoff: 104 -ln: 163, col: 0 xoff: 0 yoff: 104 -ln: 162, col: 0 xoff: 0 yoff: 104 -ln: 161, col: 0 xoff: 0 yoff: 104 -ln: 160, col: 0 xoff: 0 yoff: 104 -ln: 159, col: 0 xoff: 0 yoff: 104 -ln: 158, col: 0 xoff: 0 yoff: 104 -ln: 157, col: 0 xoff: 0 yoff: 104 -ln: 156, col: 0 xoff: 0 yoff: 104 -ln: 155, col: 0 xoff: 0 yoff: 104 -ln: 154, col: 0 xoff: 0 yoff: 104 -ln: 153, col: 0 xoff: 0 yoff: 104 -ln: 152, col: 0 xoff: 0 yoff: 104 -ln: 151, col: 0 xoff: 0 yoff: 104 -ln: 150, col: 0 xoff: 0 yoff: 104 -ln: 149, col: 0 xoff: 0 yoff: 104 -ln: 148, col: 0 xoff: 0 yoff: 104 -ln: 147, col: 0 xoff: 0 yoff: 104 -ln: 146, col: 0 xoff: 0 yoff: 104 -ln: 145, col: 0 xoff: 0 yoff: 104 -ln: 144, col: 0 xoff: 0 yoff: 104 -ln: 143, col: 0 xoff: 0 yoff: 104 -ln: 142, col: 0 xoff: 0 yoff: 104 -ln: 141, col: 0 xoff: 0 yoff: 104 -ln: 140, col: 0 xoff: 0 yoff: 104 -ln: 139, col: 0 xoff: 0 yoff: 104 -ln: 138, col: 0 xoff: 0 yoff: 104 -ln: 137, col: 0 xoff: 0 yoff: 104 -ln: 136, col: 0 xoff: 0 yoff: 104 -ln: 135, col: 0 xoff: 0 yoff: 104 -ln: 134, col: 0 xoff: 0 yoff: 104 -ln: 133, col: 0 xoff: 0 yoff: 104 -ln: 132, col: 0 xoff: 0 yoff: 104 -ln: 131, col: 0 xoff: 0 yoff: 104 -ln: 130, col: 0 xoff: 0 yoff: 104 -ln: 129, col: 0 xoff: 0 yoff: 104 -ln: 128, col: 0 xoff: 0 yoff: 104 -ln: 127, col: 0 xoff: 0 yoff: 104 -ln: 126, col: 0 xoff: 0 yoff: 104 -ln: 125, col: 0 xoff: 0 yoff: 104 -ln: 124, col: 0 xoff: 0 yoff: 104 -ln: 123, col: 0 xoff: 0 yoff: 104 -ln: 122, col: 0 xoff: 0 yoff: 104 -ln: 121, col: 0 xoff: 0 yoff: 104 -ln: 120, col: 0 xoff: 0 yoff: 104 -ln: 119, col: 0 xoff: 0 yoff: 104 -ln: 118, col: 0 xoff: 0 yoff: 104 -ln: 117, col: 0 xoff: 0 yoff: 104 -ln: 116, col: 0 xoff: 0 yoff: 104 -ln: 115, col: 0 xoff: 0 yoff: 104 -ln: 114, col: 0 xoff: 0 yoff: 104 -ln: 113, col: 0 xoff: 0 yoff: 104 -ln: 112, col: 0 xoff: 0 yoff: 104 -ln: 111, col: 0 xoff: 0 yoff: 104 -ln: 110, col: 0 xoff: 0 yoff: 104 -ln: 109, col: 0 xoff: 0 yoff: 104 -ln: 108, col: 0 xoff: 0 yoff: 104 -ln: 107, col: 0 xoff: 0 yoff: 104 -ln: 106, col: 0 xoff: 0 yoff: 104 -ln: 105, col: 0 xoff: 0 yoff: 103 -ln: 104, col: 0 xoff: 0 yoff: 102 -ln: 103, col: 0 xoff: 0 yoff: 101 -ln: 102, col: 0 xoff: 0 yoff: 100 -ln: 101, col: 0 xoff: 0 yoff: 99 -ln: 100, col: 0 xoff: 0 yoff: 98 -ln: 99, col: 0 xoff: 0 yoff: 97 -ln: 98, col: 0 xoff: 0 yoff: 96 -ln: 97, col: 0 xoff: 0 yoff: 95 -ln: 96, col: 0 xoff: 0 yoff: 94 -ln: 97, col: 0 xoff: 0 yoff: 93 -ln: 98, col: 0 xoff: 0 yoff: 93 -ln: 99, col: 0 xoff: 0 yoff: 93 -ln: 100, col: 0 xoff: 0 yoff: 93 -ln: 101, col: 0 xoff: 0 yoff: 93 -ln: 102, col: 0 xoff: 0 yoff: 93 -ln: 103, col: 0 xoff: 0 yoff: 93 -ln: 104, col: 0 xoff: 0 yoff: 93 -ln: 105, col: 0 xoff: 0 yoff: 93 -ln: 106, col: 0 xoff: 0 yoff: 93 -ln: 107, col: 0 xoff: 0 yoff: 93 -ln: 108, col: 0 xoff: 0 yoff: 93 -ln: 109, col: 0 xoff: 0 yoff: 93 -ln: 110, col: 0 xoff: 0 yoff: 93 -ln: 111, col: 0 xoff: 0 yoff: 93 -ln: 112, col: 0 xoff: 0 yoff: 93 -ln: 113, col: 0 xoff: 0 yoff: 93 -ln: 114, col: 0 xoff: 0 yoff: 93 -ln: 115, col: 0 xoff: 0 yoff: 93 -ln: 116, col: 0 xoff: 0 yoff: 93 -ln: 117, col: 0 xoff: 0 yoff: 93 -ln: 118, col: 0 xoff: 0 yoff: 93 -ln: 119, col: 0 xoff: 0 yoff: 93 -ln: 120, col: 0 xoff: 0 yoff: 93 -ln: 121, col: 0 xoff: 0 yoff: 93 -ln: 122, col: 0 xoff: 0 yoff: 93 -ln: 123, col: 0 xoff: 0 yoff: 93 -ln: 124, col: 0 xoff: 0 yoff: 93 -ln: 125, col: 0 xoff: 0 yoff: 93 -ln: 126, col: 0 xoff: 0 yoff: 93 -ln: 127, col: 0 xoff: 0 yoff: 93 -ln: 128, col: 0 xoff: 0 yoff: 93 -ln: 129, col: 0 xoff: 0 yoff: 93 -ln: 130, col: 0 xoff: 0 yoff: 93 -ln: 131, col: 0 xoff: 0 yoff: 93 -ln: 132, col: 0 xoff: 0 yoff: 93 -ln: 133, col: 0 xoff: 0 yoff: 93 -ln: 134, col: 0 xoff: 0 yoff: 93 -ln: 135, col: 0 xoff: 0 yoff: 93 -ln: 136, col: 0 xoff: 0 yoff: 93 -ln: 137, col: 0 xoff: 0 yoff: 93 -ln: 138, col: 0 xoff: 0 yoff: 93 -ln: 139, col: 0 xoff: 0 yoff: 93 -ln: 140, col: 0 xoff: 0 yoff: 93 -ln: 141, col: 0 xoff: 0 yoff: 93 -ln: 142, col: 0 xoff: 0 yoff: 93 -ln: 143, col: 0 xoff: 0 yoff: 93 -ln: 144, col: 0 xoff: 0 yoff: 93 -ln: 145, col: 0 xoff: 0 yoff: 93 -ln: 146, col: 0 xoff: 0 yoff: 93 -ln: 147, col: 0 xoff: 0 yoff: 93 -ln: 148, col: 0 xoff: 0 yoff: 93 -ln: 149, col: 0 xoff: 0 yoff: 93 -ln: 150, col: 0 xoff: 0 yoff: 93 -ln: 151, col: 0 xoff: 0 yoff: 93 -ln: 152, col: 0 xoff: 0 yoff: 93 -ln: 153, col: 0 xoff: 0 yoff: 93 -ln: 154, col: 0 xoff: 0 yoff: 93 -ln: 155, col: 0 xoff: 0 yoff: 93 -ln: 156, col: 0 xoff: 0 yoff: 94 -ln: 157, col: 0 xoff: 0 yoff: 95 -ln: 158, col: 0 xoff: 0 yoff: 96 -ln: 159, col: 0 xoff: 0 yoff: 97 -ln: 160, col: 0 xoff: 0 yoff: 98 -ln: 161, col: 0 xoff: 0 yoff: 99 -ln: 162, col: 0 xoff: 0 yoff: 100 -ln: 163, col: 0 xoff: 0 yoff: 101 -ln: 164, col: 0 xoff: 0 yoff: 102 -ln: 165, col: 0 xoff: 0 yoff: 103 -ln: 166, col: 0 xoff: 0 yoff: 104 -ln: 167, col: 0 xoff: 0 yoff: 105 -ln: 168, col: 0 xoff: 0 yoff: 106 -ln: 169, col: 0 xoff: 0 yoff: 107 -ln: 170, col: 0 xoff: 0 yoff: 108 -ln: 171, col: 0 xoff: 0 yoff: 109 -ln: 172, col: 0 xoff: 0 yoff: 110 -ln: 173, col: 0 xoff: 0 yoff: 111 -ln: 174, col: 0 xoff: 0 yoff: 112 -ln: 175, col: 0 xoff: 0 yoff: 113 -ln: 176, col: 0 xoff: 0 yoff: 114 -ln: 177, col: 0 xoff: 0 yoff: 115 -ln: 178, col: 0 xoff: 0 yoff: 116 -ln: 179, col: 0 xoff: 0 yoff: 117 -ln: 180, col: 0 xoff: 0 yoff: 118 -ln: 181, col: 0 xoff: 0 yoff: 119 -ln: 182, col: 0 xoff: 0 yoff: 120 -ln: 183, col: 0 xoff: 0 yoff: 121 -ln: 184, col: 0 xoff: 0 yoff: 122 -ln: 185, col: 0 xoff: 0 yoff: 123 -ln: 186, col: 0 xoff: 0 yoff: 124 -ln: 187, col: 0 xoff: 0 yoff: 125 -ln: 188, col: 0 xoff: 0 yoff: 126 -ln: 189, col: 0 xoff: 0 yoff: 127 -ln: 190, col: 0 xoff: 0 yoff: 128 -ln: 191, col: 0 xoff: 0 yoff: 129 -ln: 192, col: 0 xoff: 0 yoff: 130 -ln: 193, col: 0 xoff: 0 yoff: 131 -ln: 194, col: 0 xoff: 0 yoff: 132 -ln: 195, col: 0 xoff: 0 yoff: 133 -ln: 196, col: 0 xoff: 0 yoff: 134 -ln: 197, col: 0 xoff: 0 yoff: 135 -ln: 198, col: 0 xoff: 0 yoff: 136 -ln: 199, col: 0 xoff: 0 yoff: 137 -ln: 200, col: 0 xoff: 0 yoff: 138 -ln: 201, col: 0 xoff: 0 yoff: 139 -ln: 202, col: 0 xoff: 0 yoff: 140 -ln: 203, col: 0 xoff: 0 yoff: 141 -ln: 204, col: 0 xoff: 0 yoff: 142 -ln: 205, col: 0 xoff: 0 yoff: 143 -ln: 206, col: 0 xoff: 0 yoff: 144 -ln: 207, col: 0 xoff: 0 yoff: 145 -ln: 208, col: 0 xoff: 0 yoff: 146 -ln: 209, col: 0 xoff: 0 yoff: 147 -ln: 210, col: 0 xoff: 0 yoff: 148 -ln: 211, col: 0 xoff: 0 yoff: 149 -ln: 212, col: 0 xoff: 0 yoff: 150 -ln: 213, col: 0 xoff: 0 yoff: 151 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 212, col: 0 xoff: 0 yoff: 152 -ln: 211, col: 0 xoff: 0 yoff: 152 -ln: 210, col: 0 xoff: 0 yoff: 152 -ln: 211, col: 0 xoff: 0 yoff: 152 -ln: 212, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 212, col: 0 xoff: 0 yoff: 152 -ln: 211, col: 0 xoff: 0 yoff: 152 -ln: 210, col: 0 xoff: 0 yoff: 152 -ln: 209, col: 0 xoff: 0 yoff: 152 -ln: 208, col: 0 xoff: 0 yoff: 152 -ln: 207, col: 0 xoff: 0 yoff: 152 -ln: 208, col: 0 xoff: 0 yoff: 152 -ln: 209, col: 0 xoff: 0 yoff: 152 -ln: 210, col: 0 xoff: 0 yoff: 152 -ln: 209, col: 0 xoff: 0 yoff: 152 -ln: 208, col: 0 xoff: 0 yoff: 152 -ln: 207, col: 0 xoff: 0 yoff: 152 -ln: 208, col: 0 xoff: 0 yoff: 152 -ln: 209, col: 0 xoff: 0 yoff: 152 -ln: 210, col: 0 xoff: 0 yoff: 152 -ln: 209, col: 0 xoff: 0 yoff: 152 -ln: 208, col: 0 xoff: 0 yoff: 152 -ln: 207, col: 0 xoff: 0 yoff: 152 -ln: 208, col: 0 xoff: 0 yoff: 152 -ln: 209, col: 0 xoff: 0 yoff: 152 -ln: 210, col: 0 xoff: 0 yoff: 152 -ln: 211, col: 0 xoff: 0 yoff: 152 -ln: 212, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 213, col: 0 xoff: 0 yoff: 152 -ln: 212, col: 0 xoff: 0 yoff: 152 -ln: 211, col: 0 xoff: 0 yoff: 152 -ln: 210, col: 0 xoff: 0 yoff: 152 -ln: 209, col: 0 xoff: 0 yoff: 152 -ln: 208, col: 0 xoff: 0 yoff: 152 -ln: 207, col: 0 xoff: 0 yoff: 152 -ln: 206, col: 0 xoff: 0 yoff: 152 -ln: 205, col: 0 xoff: 0 yoff: 152 -ln: 204, col: 0 xoff: 0 yoff: 152 -ln: 203, col: 0 xoff: 0 yoff: 152 -ln: 202, col: 0 xoff: 0 yoff: 152 -ln: 201, col: 0 xoff: 0 yoff: 152 -ln: 200, col: 0 xoff: 0 yoff: 152 -ln: 199, col: 0 xoff: 0 yoff: 152 -ln: 198, col: 0 xoff: 0 yoff: 152 -ln: 197, col: 0 xoff: 0 yoff: 152 -ln: 196, col: 0 xoff: 0 yoff: 152 -ln: 195, col: 0 xoff: 0 yoff: 152 -ln: 194, col: 0 xoff: 0 yoff: 152 -ln: 193, col: 0 xoff: 0 yoff: 152 -ln: 192, col: 0 xoff: 0 yoff: 152 -ln: 191, col: 0 xoff: 0 yoff: 152 -ln: 190, col: 0 xoff: 0 yoff: 152 -ln: 189, col: 0 xoff: 0 yoff: 152 -ln: 188, col: 0 xoff: 0 yoff: 152 -ln: 187, col: 0 xoff: 0 yoff: 152 -ln: 186, col: 0 xoff: 0 yoff: 152 -ln: 185, col: 0 xoff: 0 yoff: 152 -ln: 184, col: 0 xoff: 0 yoff: 152 -ln: 183, col: 0 xoff: 0 yoff: 152 -ln: 182, col: 0 xoff: 0 yoff: 152 -ln: 181, col: 0 xoff: 0 yoff: 152 -ln: 180, col: 0 xoff: 0 yoff: 152 -ln: 179, col: 0 xoff: 0 yoff: 152 -ln: 178, col: 0 xoff: 0 yoff: 152 -ln: 177, col: 0 xoff: 0 yoff: 152 -ln: 176, col: 0 xoff: 0 yoff: 152 -ln: 175, col: 0 xoff: 0 yoff: 152 -ln: 174, col: 0 xoff: 0 yoff: 152 -ln: 173, col: 0 xoff: 0 yoff: 152 -ln: 172, col: 0 xoff: 0 yoff: 152 -ln: 171, col: 0 xoff: 0 yoff: 152 -ln: 170, col: 0 xoff: 0 yoff: 152 -ln: 169, col: 0 xoff: 0 yoff: 152 -ln: 168, col: 0 xoff: 0 yoff: 152 -ln: 167, col: 0 xoff: 0 yoff: 152 -ln: 166, col: 0 xoff: 0 yoff: 152 -ln: 165, col: 0 xoff: 0 yoff: 152 -ln: 164, col: 0 xoff: 0 yoff: 152 -ln: 163, col: 0 xoff: 0 yoff: 152 -ln: 162, col: 0 xoff: 0 yoff: 152 -ln: 161, col: 0 xoff: 0 yoff: 152 -ln: 160, col: 0 xoff: 0 yoff: 152 -ln: 159, col: 0 xoff: 0 yoff: 152 -ln: 158, col: 0 xoff: 0 yoff: 152 -ln: 157, col: 0 xoff: 0 yoff: 152 -ln: 156, col: 0 xoff: 0 yoff: 152 -ln: 155, col: 0 xoff: 0 yoff: 152 -ln: 154, col: 0 xoff: 0 yoff: 152 -ln: 153, col: 0 xoff: 0 yoff: 151 -ln: 152, col: 0 xoff: 0 yoff: 150 -ln: 151, col: 0 xoff: 0 yoff: 149 -ln: 150, col: 0 xoff: 0 yoff: 148 -ln: 149, col: 0 xoff: 0 yoff: 147 -ln: 148, col: 0 xoff: 0 yoff: 146 -ln: 147, col: 0 xoff: 0 yoff: 145 -ln: 146, col: 0 xoff: 0 yoff: 144 -ln: 145, col: 0 xoff: 0 yoff: 143 -ln: 144, col: 0 xoff: 0 yoff: 142 -ln: 143, col: 0 xoff: 0 yoff: 141 -ln: 142, col: 0 xoff: 0 yoff: 140 -ln: 141, col: 0 xoff: 0 yoff: 139 -ln: 140, col: 0 xoff: 0 yoff: 138 -ln: 139, col: 0 xoff: 0 yoff: 137 -ln: 138, col: 0 xoff: 0 yoff: 136 -ln: 137, col: 0 xoff: 0 yoff: 135 -ln: 136, col: 0 xoff: 0 yoff: 134 -ln: 135, col: 0 xoff: 0 yoff: 133 -ln: 134, col: 0 xoff: 0 yoff: 132 -ln: 133, col: 0 xoff: 0 yoff: 131 -ln: 132, col: 0 xoff: 0 yoff: 130 -ln: 131, col: 0 xoff: 0 yoff: 129 -ln: 130, col: 0 xoff: 0 yoff: 128 -ln: 129, col: 0 xoff: 0 yoff: 127 -ln: 128, col: 0 xoff: 0 yoff: 126 -ln: 127, col: 0 xoff: 0 yoff: 125 -ln: 126, col: 0 xoff: 0 yoff: 124 -ln: 125, col: 0 xoff: 0 yoff: 123 -ln: 124, col: 0 xoff: 0 yoff: 122 -ln: 123, col: 0 xoff: 0 yoff: 121 -ln: 122, col: 0 xoff: 0 yoff: 120 -ln: 121, col: 0 xoff: 0 yoff: 119 -ln: 120, col: 0 xoff: 0 yoff: 118 -ln: 119, col: 0 xoff: 0 yoff: 117 -ln: 118, col: 0 xoff: 0 yoff: 116 -ln: 117, col: 0 xoff: 0 yoff: 115 -ln: 116, col: 0 xoff: 0 yoff: 114 -ln: 115, col: 0 xoff: 0 yoff: 113 -ln: 114, col: 0 xoff: 0 yoff: 112 -ln: 113, col: 0 xoff: 0 yoff: 111 -ln: 112, col: 0 xoff: 0 yoff: 110 -ln: 111, col: 0 xoff: 0 yoff: 109 -ln: 110, col: 0 xoff: 0 yoff: 108 -ln: 109, col: 0 xoff: 0 yoff: 107 -ln: 108, col: 0 xoff: 0 yoff: 106 -ln: 107, col: 0 xoff: 0 yoff: 105 -ln: 106, col: 0 xoff: 0 yoff: 104 -ln: 105, col: 0 xoff: 0 yoff: 103 -ln: 104, col: 0 xoff: 0 yoff: 102 -ln: 103, col: 0 xoff: 0 yoff: 101 -ln: 102, col: 0 xoff: 0 yoff: 100 -ln: 101, col: 0 xoff: 0 yoff: 99 -ln: 100, col: 0 xoff: 0 yoff: 98 -ln: 99, col: 0 xoff: 0 yoff: 97 -ln: 98, col: 0 xoff: 0 yoff: 96 -ln: 97, col: 0 xoff: 0 yoff: 95 -ln: 96, col: 0 xoff: 0 yoff: 94 -ln: 95, col: 0 xoff: 0 yoff: 93 -ln: 94, col: 0 xoff: 0 yoff: 92 -ln: 93, col: 0 xoff: 0 yoff: 91 -ln: 92, col: 0 xoff: 0 yoff: 90 -ln: 91, col: 0 xoff: 0 yoff: 89 -ln: 90, col: 0 xoff: 0 yoff: 88 -ln: 89, col: 0 xoff: 0 yoff: 87 -ln: 88, col: 0 xoff: 0 yoff: 86 -ln: 87, col: 0 xoff: 0 yoff: 85 -ln: 86, col: 0 xoff: 0 yoff: 84 -ln: 85, col: 0 xoff: 0 yoff: 83 -ln: 84, col: 0 xoff: 0 yoff: 82 -ln: 83, col: 0 xoff: 0 yoff: 81 -ln: 82, col: 0 xoff: 0 yoff: 80 -ln: 81, col: 0 xoff: 0 yoff: 79 -ln: 80, col: 0 xoff: 0 yoff: 78 -ln: 79, col: 0 xoff: 0 yoff: 77 -ln: 78, col: 0 xoff: 0 yoff: 76 -ln: 77, col: 0 xoff: 0 yoff: 75 -ln: 76, col: 0 xoff: 0 yoff: 74 -ln: 75, col: 0 xoff: 0 yoff: 73 -ln: 74, col: 0 xoff: 0 yoff: 72 -ln: 73, col: 0 xoff: 0 yoff: 71 -ln: 72, col: 0 xoff: 0 yoff: 70 -ln: 71, col: 0 xoff: 0 yoff: 69 -ln: 70, col: 0 xoff: 0 yoff: 68 -ln: 69, col: 0 xoff: 0 yoff: 67 -ln: 68, col: 0 xoff: 0 yoff: 66 -ln: 67, col: 0 xoff: 0 yoff: 65 -ln: 66, col: 0 xoff: 0 yoff: 64 -ln: 65, col: 0 xoff: 0 yoff: 63 -ln: 64, col: 0 xoff: 0 yoff: 62 -ln: 63, col: 0 xoff: 0 yoff: 61 -ln: 62, col: 0 xoff: 0 yoff: 60 -ln: 61, col: 0 xoff: 0 yoff: 59 -ln: 60, col: 0 xoff: 0 yoff: 58 -ln: 59, col: 0 xoff: 0 yoff: 57 -ln: 58, col: 0 xoff: 0 yoff: 56 -ln: 57, col: 0 xoff: 0 yoff: 55 -ln: 56, col: 0 xoff: 0 yoff: 54 -ln: 55, col: 0 xoff: 0 yoff: 53 -ln: 54, col: 0 xoff: 0 yoff: 52 -ln: 53, col: 0 xoff: 0 yoff: 51 -ln: 52, col: 0 xoff: 0 yoff: 50 -ln: 51, col: 0 xoff: 0 yoff: 49 -ln: 50, col: 0 xoff: 0 yoff: 48 -ln: 49, col: 0 xoff: 0 yoff: 47 -ln: 48, col: 0 xoff: 0 yoff: 46 -ln: 47, col: 0 xoff: 0 yoff: 45 -ln: 46, col: 0 xoff: 0 yoff: 44 -ln: 45, col: 0 xoff: 0 yoff: 43 -ln: 44, col: 0 xoff: 0 yoff: 42 -ln: 43, col: 0 xoff: 0 yoff: 41 -ln: 42, col: 0 xoff: 0 yoff: 40 -ln: 41, col: 0 xoff: 0 yoff: 39 -ln: 40, col: 0 xoff: 0 yoff: 38 -ln: 39, col: 0 xoff: 0 yoff: 37 -ln: 38, col: 0 xoff: 0 yoff: 36 -ln: 37, col: 0 xoff: 0 yoff: 35 -ln: 36, col: 0 xoff: 0 yoff: 34 -ln: 35, col: 0 xoff: 0 yoff: 33 -ln: 34, col: 0 xoff: 0 yoff: 32 -ln: 33, col: 0 xoff: 0 yoff: 31 -ln: 32, col: 0 xoff: 0 yoff: 30 -ln: 31, col: 0 xoff: 0 yoff: 29 -ln: 30, col: 0 xoff: 0 yoff: 28 -ln: 29, col: 0 xoff: 0 yoff: 27 -ln: 28, col: 0 xoff: 0 yoff: 26 -ln: 27, col: 0 xoff: 0 yoff: 25 -ln: 26, col: 0 xoff: 0 yoff: 24 -ln: 25, col: 0 xoff: 0 yoff: 23 -ln: 24, col: 0 xoff: 0 yoff: 22 -ln: 23, col: 0 xoff: 0 yoff: 21 -ln: 22, col: 0 xoff: 0 yoff: 20 -ln: 21, col: 0 xoff: 0 yoff: 19 -ln: 20, col: 0 xoff: 0 yoff: 18 -ln: 19, col: 0 xoff: 0 yoff: 17 -ln: 18, col: 0 xoff: 0 yoff: 16 -ln: 17, col: 0 xoff: 0 yoff: 15 -ln: 16, col: 0 xoff: 0 yoff: 14 -ln: 15, col: 0 xoff: 0 yoff: 13 -ln: 14, col: 0 xoff: 0 yoff: 12 -ln: 13, col: 0 xoff: 0 yoff: 11 -ln: 12, col: 0 xoff: 0 yoff: 10 -ln: 11, col: 0 xoff: 0 yoff: 9 -ln: 10, col: 0 xoff: 0 yoff: 8 -ln: 9, col: 0 xoff: 0 yoff: 7 -ln: 8, col: 0 xoff: 0 yoff: 6 -ln: 7, col: 0 xoff: 0 yoff: 5 -ln: 6, col: 0 xoff: 0 yoff: 4 -ln: 5, col: 0 xoff: 0 yoff: 3 - diff --git a/final/main.c b/final/main.c index 23ddd35..e3760f5 100644 --- a/final/main.c +++ b/final/main.c @@ -6,12 +6,13 @@ void help() { printf("Usage:\n"); - printf(" cmd open // opens the specified file\n"); - printf(" cmd rm // deletes the specified file\n"); - printf(" cmd new // creates a new empty file at the specified path\n"); - printf(" cmd mv // moves the specified file to the new path\n"); - printf(" cmd cp // copies the specified file to the new path\n"); - printf(" cmd len // returns the length of the specified file\n"); + printf(" cmd open // opens the specified file\n"); + printf(" cmd rm // deletes the specified file\n"); + printf(" cmd new // creates a new empty file at the specified path\n"); + printf(" cmd mv // moves the specified file to the new path\n"); + printf(" cmd cp // copies the specified file to the new path\n"); + printf(" cmd lenl // returns the length of the specified file in lines\n"); + printf(" cmd lenc // returns the length of the specified file in chars\n"); printf(" cmd log // prints a list of all changes made to the file\n"); } @@ -33,9 +34,9 @@ int open_editor(char* filename) { Editor editor; if (strcmp(filename, "") == 0) { - editor = new_editor(); + editor = new_editor(str_from_chars(filename)); } else { - editor = editor_from(str_from_file(fopen(filename, "r"))); + editor = editor_from(str_from_file(fopen(filename, "r")), str_from_chars(filename)); } while (true) { refresh(); @@ -68,7 +69,6 @@ int open_editor(char* filename) { move_cursor(&editor, 1, 0); break; default: - fprintf(stderr, "%d\n", c); addchar(&editor, c); break; } @@ -76,12 +76,15 @@ int open_editor(char* filename) { switch (c) { case 'q': endwin(); + shutdown_editor(&editor); + return 0; case 'i': switch_mode(&editor); break; case 'w': editor.unsaved_changes = false; + save_file(&editor); // TODO: write function to save the data to a file break; default: @@ -91,16 +94,161 @@ int open_editor(char* filename) { } } -int main(int argc, char* argv[]) { - if (argc == 1) { - Editor e = new_editor(); - open_editor(""); - } else if (argc == 3) { - if (strcmp(argv[1], "open") == 0) { - fprintf(stderr, "starting editor openiing file %s", argv[2]); - open_editor(argv[2]); - } +// Copies a file from source to destination +int copy_file(const char* source, const char* dest) { + FILE* src = fopen(source, "rb"); + if (src == NULL) { + fprintf(stderr, "Error: Could not open source file %s\n", source); + return -1; } + + FILE* dst = fopen(dest, "wb"); + if (dst == NULL) { + fclose(src); + fprintf(stderr, "Error: Could not create destination file %s\n", dest); + return -1; + } + + char buffer[8192]; + size_t bytes; + while ((bytes = fread(buffer, 1, sizeof(buffer), src)) > 0) { + if (fwrite(buffer, 1, bytes, dst) != bytes) { + fclose(src); + fclose(dst); + fprintf(stderr, "Error: Write failed to %s\n", dest); + return -1; + } + } + + fclose(src); + fclose(dst); return 0; } +// Counts the number of characters in a file +int count_chars(const char* filename) { + FILE* file = fopen(filename, "r"); + if (file == NULL) { + fprintf(stderr, "Error: Could not open file %s\n", filename); + return -1; + } + + int count = 0; + int ch; + while ((ch = fgetc(file)) != EOF) { + count++; + } + + fclose(file); + return count; +} + +// Counts the number of lines in a file +int count_lines(const char* filename) { + FILE* file = fopen(filename, "r"); + if (file == NULL) { + fprintf(stderr, "Error: Could not open file %s\n", filename); + return -1; + } + + int count = 0; + int ch; + int last_char = '\n'; // Handle empty files as 0 lines + + while ((ch = fgetc(file)) != EOF) { + if (ch == '\n') { + count++; + } + last_char = ch; + } + + // Count last line if it doesn't end with newline + if (last_char != '\n' && last_char != EOF) { + count++; + } + + fclose(file); + return count; +} + +// Command types +typedef enum { + CMD_OPEN, + CMD_RM, + CMD_NEW, + CMD_MV, + CMD_CP, + CMD_LENL, + CMD_LENC, + CMD_LOG, + CMD_UNKNOWN +} CommandType; + +// Convert string to command type +CommandType str_to_cmd(const char* cmd) { + if (strcmp(cmd, "open") == 0) return CMD_OPEN; + if (strcmp(cmd, "rm") == 0) return CMD_RM; + if (strcmp(cmd, "new") == 0) return CMD_NEW; + if (strcmp(cmd, "mv") == 0) return CMD_MV; + if (strcmp(cmd, "cp") == 0) return CMD_CP; + if (strcmp(cmd, "lenl") == 0) return CMD_LENL; + if (strcmp(cmd, "lenc") == 0) return CMD_LENC; + if (strcmp(cmd, "log") == 0) return CMD_LOG; + return CMD_UNKNOWN; +} + +int main(int argc, char* argv[]) { + + if (argc < 2) { + help(); + return 1; + } + + CommandType cmd = str_to_cmd(argv[1]); + + if (argc == 3) { + switch (cmd) { + case CMD_OPEN: + open_editor(argv[2]); + break; + case CMD_RM: + remove(argv[2]); + break; + case CMD_NEW: + open_editor(argv[2]); + break; + case CMD_LENC: + printf("%d\n", count_chars(argv[2])); + break; + case CMD_LENL: + printf("%d\n", count_lines(argv[2])); + break; + default: + help(); + } + } + + else if (argc == 4) { + switch (cmd) { + case CMD_MV: + if (rename(argv[2], argv[3]) != 0) { + fprintf(stderr, "Error: Failed to move file %s to %s\n", argv[2], argv[3]); + } + break; + case CMD_CP: + if (copy_file(argv[2], argv[3]) != 0) { + fprintf(stderr, "Error: Failed to copy file %s to %s\n", argv[2], argv[3]); + } + break; + default: + help(); + } + } + + else { + printf("invalid options\n"); + help(); + } + + return 0; +}