persist.c (8999B)
1 /* persist.c - guardar y cargar el estado: texto "clave=valor" por linea. 2 * Los numeros van como "a,b,r,d,p|decimal" (exacto + decimal). */ 3 #include <string.h> 4 #include "calc.h" 5 #include "str.h" 6 7 static int put_kv(char *buf, int n, int p, const char *k, const char *v) 8 { 9 if (p < 0) return p; 10 p = str_put(buf, n, p, k); 11 p = str_put(buf, n, p, "="); 12 p = str_put(buf, n, p, v); 13 return str_put(buf, n, p, "\n"); 14 } 15 16 static int put_ki(char *buf, int n, int p, const char *k, long v) 17 { 18 char b[24]; 19 str_int(b, sizeof b, 0, v); 20 return put_kv(buf, n, p, k, b); 21 } 22 23 static void num_text(const Num *v, char *b, int n) 24 { 25 int p = 0; 26 if (v->exact) { 27 p = i64_text(b, n, p, v->x.a); p = str_put(b, n, p, ","); 28 p = i64_text(b, n, p, v->x.b); p = str_put(b, n, p, ","); 29 p = i64_text(b, n, p, v->x.r); p = str_put(b, n, p, ","); 30 p = i64_text(b, n, p, v->x.d); p = str_put(b, n, p, ","); 31 p = str_int(b, n, p, v->x.p); 32 } 33 p = str_put(b, n, p, "|"); 34 dec_to_text(v->v, b + p, n - p); 35 } 36 37 /* int64 sin stdio */ 38 static const char *scan_i64(const char *s, int64_t *out) 39 { 40 bool neg = *s == '-'; 41 if (neg || *s == '+') s++; 42 if (*s < '0' || *s > '9') return 0; 43 uint64_t v = 0; 44 while (*s >= '0' && *s <= '9') v = v * 10 + (uint64_t)(*s++ - '0'); 45 *out = neg ? -(int64_t)v : (int64_t)v; 46 return s; 47 } 48 49 static Num text_num(const char *s) 50 { 51 Num r = num_dec(dec_parse(strchr(s, '|') ? strchr(s, '|') + 1 : s, 0)); 52 if (*s != '|') { 53 int64_t f[5]; 54 const char *p = s; 55 for (int i = 0; i < 5 && p; i++) { 56 p = scan_i64(p, &f[i]); 57 if (p && i < 4) { if (*p != ',') p = 0; else p++; } 58 } 59 if (p) { 60 Ex x = { f[0], f[1], f[2], f[3], (uint8_t)f[4] }; 61 if (ex_norm(&x)) r = num_ex(&x); 62 } 63 } 64 return r; 65 } 66 67 int persist_save(const Calc *c, char *buf, int n) 68 { 69 char b[300]; 70 int p = 0; 71 buf[0] = 0; 72 p = put_kv(buf, n, p, "calc", "1"); 73 p = put_ki(buf, n, p, "ang", c->ang); 74 p = put_ki(buf, n, p, "dec", c->dec_default); 75 p = put_ki(buf, n, p, "fmt", c->fmt.mode); 76 p = put_ki(buf, n, p, "fmtn", c->fmt.n); 77 p = put_ki(buf, n, p, "mixed", c->fmt.mixed); 78 p = put_ki(buf, n, p, "digits", c->fmt.digits); 79 p = put_ki(buf, n, p, "mode", c->mode); 80 p = put_ki(buf, n, p, "base", c->pg.base); 81 p = put_ki(buf, n, p, "bits", c->pg.bits); 82 for (int i = 0; i < NVARS; i++) { 83 char k[8] = "var"; 84 k[3] = VAR_NAMES[i]; k[4] = 0; 85 num_text(&c->vars[i], b, sizeof b); 86 p = put_kv(buf, n, p, k, b); 87 } 88 num_text(&c->ans, b, sizeof b); p = put_kv(buf, n, p, "ans", b); 89 if (c->ans_im.v.m) { num_text(&c->ans_im, b, sizeof b); p = put_kv(buf, n, p, "ansi", b); } 90 for (int i = 0; i < NVARS; i++) { 91 if (!c->vars_im[i].v.m) continue; 92 char k[8] = "vari"; 93 k[4] = VAR_NAMES[i]; k[5] = 0; 94 num_text(&c->vars_im[i], b, sizeof b); 95 p = put_kv(buf, n, p, k, b); 96 } 97 num_text(&c->preans, b, sizeof b); p = put_kv(buf, n, p, "preans", b); 98 for (int i = 0; i < c->nh; i++) { 99 expr_to_text(&c->hist[i].in, b, sizeof b); 100 p = put_kv(buf, n, p, "h", b); 101 num_text(&c->hist[i].res, b, sizeof b); 102 int q = (int)strlen(b); 103 str_put(b, sizeof b, q, c->hist[i].err ? "|e" : ""); 104 p = put_kv(buf, n, p, "hr", b); 105 if (c->hist[i].res_im.v.m || c->hist[i].polar) { 106 num_text(&c->hist[i].res_im, b, sizeof b); 107 p = put_kv(buf, n, p, c->hist[i].polar ? "hp" : "hi", b); 108 } 109 } 110 /* matrices y vectores: "mA=filas,columnas;n;n;..." */ 111 for (int k = 0; k < 8; k++) { 112 const MVal *m = k < 4 ? &c->mt.m[k] : &c->mt.v[k - 4]; 113 if (!m->r) continue; 114 static char mb[1600]; 115 int q = str_int(mb, sizeof mb, 0, m->r); 116 q = str_put(mb, sizeof mb, q, ","); 117 q = str_int(mb, sizeof mb, q, m->c); 118 for (int i = 0; i < m->r; i++) 119 for (int j = 0; j < m->c; j++) { 120 num_text(mv_get(m, i, j), b, sizeof b); 121 q = str_put(mb, sizeof mb, q, ";"); 122 q = str_put(mb, sizeof mb, q, b); 123 } 124 char key[3] = { k < 4 ? 'm' : 'v', (char)('A' + k % 4), 0 }; 125 p = put_kv(buf, n, p, key, mb); 126 } 127 /* estadistica */ 128 p = put_ki(buf, n, p, "stt", c->st.type); 129 for (int i = 0; i < c->st.n; i++) { 130 num_text(&c->st.x[i], b, sizeof b); p = put_kv(buf, n, p, "sx", b); 131 num_text(&c->st.y[i], b, sizeof b); p = put_kv(buf, n, p, "sy", b); 132 } 133 expr_to_text(&c->tb.f, b, sizeof b); p = put_kv(buf, n, p, "tf", b); 134 expr_to_text(&c->tb.g, b, sizeof b); p = put_kv(buf, n, p, "tg", b); 135 expr_to_text(&c->gr.f, b, sizeof b); p = put_kv(buf, n, p, "gf", b); 136 expr_to_text(&c->gr.g, b, sizeof b); p = put_kv(buf, n, p, "gg", b); 137 return p < 0 ? (int)strlen(buf) : p; 138 } 139 140 void persist_load(Calc *c, const char *buf, int n) 141 { 142 static char line[320]; 143 const char *p = buf, *end = buf + n; 144 int hcount = 0; 145 while (p < end && *p) { 146 int k = 0; 147 while (p < end && *p && *p != '\n' && k < (int)sizeof line - 1) line[k++] = *p++; 148 line[k] = 0; 149 while (p < end && *p && *p != '\n') p++; 150 if (p < end && *p == '\n') p++; 151 char *eq = strchr(line, '='); 152 if (!eq) continue; 153 *eq = 0; 154 const char *key = line, *val = eq + 1; 155 long v = 0; 156 bool num = str_scan_int(val, &v) != 0; 157 if (!strcmp(key, "ang") && num && v >= 0 && v < 3) c->ang = (int)v; 158 else if (!strcmp(key, "dec") && num) c->dec_default = v != 0; 159 else if (!strcmp(key, "fmt") && num && v >= 0 && v <= FMT_ENG) c->fmt.mode = (uint8_t)v; 160 else if (!strcmp(key, "fmtn") && num && v >= 0 && v <= 9) c->fmt.n = (uint8_t)v; 161 else if (!strcmp(key, "mixed") && num) c->fmt.mixed = v != 0; 162 else if (!strcmp(key, "digits") && num && (v == 10 || v == 12 || v == 15)) c->fmt.digits = (uint8_t)v; 163 else if (!strcmp(key, "mode") && num && v >= 0 && v < NMODES) c->mode = (int)v; 164 else if (!strcmp(key, "base") && num && (v == 2 || v == 8 || v == 10 || v == 16)) c->pg.base = (int)v; 165 else if (!strcmp(key, "bits") && num && (v == 32 || v == 64 || v == 16 || v == 8)) c->pg.bits = (int)v; 166 else if (!strncmp(key, "vari", 4) && key[4] && !key[5]) { 167 const char *vn = strchr(VAR_NAMES, key[4]); 168 if (vn) c->vars_im[vn - VAR_NAMES] = text_num(val); 169 } else if (!strcmp(key, "ansi")) c->ans_im = text_num(val); 170 else if ((!strcmp(key, "hi") || !strcmp(key, "hp")) && hcount > 0) { 171 c->hist[hcount - 1].res_im = text_num(val); 172 c->hist[hcount - 1].polar = key[1] == 'p'; 173 } 174 else if (!strncmp(key, "var", 3) && key[3] && !key[4]) { 175 const char *vn = strchr(VAR_NAMES, key[3]); 176 if (vn) c->vars[vn - VAR_NAMES] = text_num(val); 177 } else if ((key[0] == 'm' || key[0] == 'v') && key[1] >= 'A' && key[1] <= 'D' && !key[2]) { 178 MVal *m = key[0] == 'm' ? &c->mt.m[key[1] - 'A'] : &c->mt.v[key[1] - 'A']; 179 long r = 0, cc = 0; 180 const char *q = str_scan_int(val, &r); 181 if (!q || *q != ',' || !(q = str_scan_int(q + 1, &cc)) || r < 1 || r > MAT_N || cc < 1 || cc > MAT_N) continue; 182 mv_zero(m, key[0] == 'm' ? MV_MAT : MV_VEC, (int)r, (int)cc); 183 for (int i = 0; i < r * cc && *q == ';'; i++) { 184 q++; 185 static char one[300]; 186 int l = 0; 187 while (q[l] && q[l] != ';' && l < (int)sizeof one - 1) { one[l] = q[l]; l++; } 188 one[l] = 0; 189 *mv_at(m, (int)(i / cc), (int)(i % cc)) = text_num(one); 190 q += l; 191 } 192 } else if (!strcmp(key, "ans")) c->ans = text_num(val); 193 else if (!strcmp(key, "preans")) c->preans = text_num(val); 194 else if (!strcmp(key, "h") && hcount < HIST_MAX) { 195 if (expr_from_text(&c->hist[hcount].in, val)) hcount++; 196 } else if (!strcmp(key, "hr") && hcount > 0) { 197 Hist *h = &c->hist[hcount - 1]; 198 h->res = text_num(val); 199 h->err = strstr(val, "|e") != 0; 200 h->dec = 0; 201 h->res_im = num_int(0); 202 h->polar = 0; 203 } else if (!strcmp(key, "stt") && num && v >= 0 && v < ST_NTYPES) c->st.type = (int)v; 204 else if (!strcmp(key, "sx") && c->st.n < STAT_MAX) { c->st.x[c->st.n] = text_num(val); c->st.y[c->st.n] = num_int(0); c->st.n++; } 205 else if (!strcmp(key, "sy") && c->st.n > 0) c->st.y[c->st.n - 1] = text_num(val); 206 else if (!strcmp(key, "tf")) expr_from_text(&c->tb.f, val); 207 else if (!strcmp(key, "tg")) expr_from_text(&c->tb.g, val); 208 else if (!strcmp(key, "gf")) expr_from_text(&c->gr.f, val); 209 else if (!strcmp(key, "gg")) expr_from_text(&c->gr.g, val); 210 } 211 c->nh = hcount; 212 c->hsel = -1; 213 }