snapkitty
compilers
c
python
nasm
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;
    }
}