File size: 4,203 Bytes
9abace2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | /*
* Sovereign IDE — Document
* File open/save with UTF-8 + CRLF normalization.
*/
#include "document.h"
#include <windows.h>
#include <stdlib.h>
#include <string.h>
void document_new(Document *doc) {
doc->buffer = buffer_create("", 0);
doc->path[0] = L'\0';
doc->has_path = false;
doc->line_ending = LE_LF;
}
static LineEnding detect_line_ending(const char *text, size_t len) {
for (size_t i = 0; i + 1 < len; i++) {
if (text[i] == '\r' && text[i+1] == '\n') return LE_CRLF;
}
for (size_t i = 0; i < len; i++) {
if (text[i] == '\r') return LE_CR;
}
return LE_LF;
}
/* Normalize CRLF/CR to LF for internal storage */
static char *normalize_to_lf(const char *src, size_t src_len, size_t *out_len) {
char *dst = (char *)malloc(src_len + 1);
size_t j = 0;
for (size_t i = 0; i < src_len; i++) {
if (src[i] == '\r') {
dst[j++] = '\n';
if (i + 1 < src_len && src[i+1] == '\n') i++;
} else {
dst[j++] = src[i];
}
}
dst[j] = '\0';
*out_len = j;
return dst;
}
SovResult document_open(Document *doc, const wchar_t *path) {
HANDLE h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) return SOV_ERR_IO;
LARGE_INTEGER file_size;
GetFileSizeEx(h, &file_size);
if (file_size.QuadPart > 256 * 1024 * 1024) {
CloseHandle(h);
return SOV_ERR_IO;
}
size_t sz = (size_t)file_size.QuadPart;
char *raw = (char *)malloc(sz + 1);
DWORD nread = 0;
ReadFile(h, raw, (DWORD)sz, &nread, NULL);
CloseHandle(h);
raw[nread] = '\0';
/* Skip UTF-8 BOM if present */
const char *text = raw;
size_t text_len = nread;
if (text_len >= 3 &&
(unsigned char)text[0] == 0xEF &&
(unsigned char)text[1] == 0xBB &&
(unsigned char)text[2] == 0xBF) {
text += 3;
text_len -= 3;
}
doc->line_ending = detect_line_ending(text, text_len);
size_t norm_len;
char *norm = normalize_to_lf(text, text_len, &norm_len);
free(raw);
doc->buffer = buffer_create(norm, norm_len);
free(norm);
wcscpy_s(doc->path, MAX_PATH, path);
doc->has_path = true;
return SOV_OK;
}
SovResult document_save(Document *doc) {
if (!doc->has_path) return SOV_ERR_IO;
SovResult r = document_save_as(doc, doc->path);
return r;
}
SovResult document_save_as(Document *doc, const wchar_t *path) {
size_t len = buffer_length(doc->buffer);
char *content = (char *)malloc(len + 1);
buffer_read(doc->buffer, 0, content, len);
content[len] = '\0';
/* Re-apply original line endings */
char *output = content;
size_t out_len = len;
char *crlf_buf = NULL;
if (doc->line_ending == LE_CRLF) {
/* count newlines to allocate */
size_t nl = 0;
for (size_t i = 0; i < len; i++) if (content[i] == '\n') nl++;
crlf_buf = (char *)malloc(len + nl + 1);
size_t j = 0;
for (size_t i = 0; i < len; i++) {
if (content[i] == '\n') crlf_buf[j++] = '\r';
crlf_buf[j++] = content[i];
}
crlf_buf[j] = '\0';
output = crlf_buf;
out_len = j;
}
HANDLE h = CreateFileW(path, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {
free(content);
free(crlf_buf);
return SOV_ERR_IO;
}
DWORD written;
WriteFile(h, output, (DWORD)out_len, &written, NULL);
CloseHandle(h);
free(content);
free(crlf_buf);
wcscpy_s(doc->path, MAX_PATH, path);
doc->has_path = true;
buffer_mark_clean(doc->buffer);
return SOV_OK;
}
bool document_is_dirty(const Document *doc) {
return buffer_is_dirty(doc->buffer);
}
void document_destroy(Document *doc) {
if (doc->buffer) {
buffer_destroy(doc->buffer);
doc->buffer = NULL;
}
}
|