1: /*
2: * $Id: fma_gedit.c,v 1.7 2025/04/13 04:22:43 snw Exp $
3: * FreeM global editor
4: *
5: *
6: * Author: Serena Willis <snw@coherent-logic.com>
7: * Copyright (C) 1998 MUG Deutschland
8: * Copyright (C) 2023, 2025 Coherent Logic Development LLC
9: *
10: *
11: * This file is part of FreeM.
12: *
13: * FreeM is free software: you can redistribute it and/or modify
14: * it under the terms of the GNU Affero Public License as published by
15: * the Free Software Foundation, either version 3 of the License, or
16: * (at your option) any later version.
17: *
18: * FreeM is distributed in the hope that it will be useful,
19: * but WITHOUT ANY WARRANTY; without even the implied warranty of
20: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21: * GNU Affero Public License for more details.
22: *
23: * You should have received a copy of the GNU Affero Public License
24: * along with FreeM. If not, see <https://www.gnu.org/licenses/>.
25: *
26: * $Log: fma_gedit.c,v $
27: * Revision 1.7 2025/04/13 04:22:43 snw
28: * Fix snprintf calls
29: *
30: * Revision 1.6 2025/04/09 19:52:02 snw
31: * Eliminate as many warnings as possible while building with -Wall
32: *
33: * Revision 1.5 2025/03/31 16:33:56 snw
34: * Work on fmadm edit global
35: *
36: * Revision 1.4 2025/03/30 01:36:58 snw
37: * Make it easier to bring back fma_gedit, fix double-free in global handler, limit $CHAR to 7-bit ASCII
38: *
39: * Revision 1.3 2025/03/09 19:14:24 snw
40: * First phase of REUSE compliance and header reformat
41: *
42: *
43: * SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
44: * SPDX-License-Identifier: AGPL-3.0-or-later
45: **/
46:
47: #include <stdlib.h>
48: #include <stdio.h>
49: #include <stdlib.h>
50: #include <string.h>
51: #include <dirent.h>
52: #include <time.h>
53: #include <unistd.h>
54: #include <sys/types.h>
55: #include <sys/stat.h>
56: #include <fcntl.h>
57:
58: #include "fmadm.h"
59: #include "fma_globals.h"
60:
61: #ifdef HAVE_LIBREADLINE
62: # if defined(HAVE_READLINE_READLINE_H)
63: # include <readline/readline.h>
64: # elif defined(HAVE_READLINE_H)
65: # include <readline.h>
66: # else /* !defined(HAVE_READLINE_H) */
67: extern char *readline ();
68: # endif /* !defined(HAVE_READLINE_H) */
69: /*char *cmdline = NULL;*/
70: #else /* !defined(HAVE_READLINE_READLINE_H) */
71: /* no readline */
72: #endif /* HAVE_LIBREADLINE */
73:
74: #ifdef HAVE_READLINE_HISTORY
75: # if defined(HAVE_READLINE_HISTORY_H)
76: # include <readline/history.h>
77: # elif defined(HAVE_HISTORY_H)
78: # include <history.h>
79: # else /* !defined(HAVE_HISTORY_H) */
80: extern void add_history ();
81: extern int write_history ();
82: extern int read_history ();
83: # endif /* defined(HAVE_READLINE_HISTORY_H) */
84: /* no history */
85: #endif /* HAVE_READLINE_HISTORY */
86:
87:
88:
89: #define GE_MXGBL 100
90:
91: typedef struct ge_key {
92: char key[256];
93: char data[256];
94:
95: struct ge_key *next;
96: } ge_key;
97:
98: typedef struct ge_blockinfo {
99:
100: int keylen;
101: int keyoffs;
102: char key[STRLEN];
103: int datalen;
104: char data[STRLEN];
105:
106: long llptr;
107: long rlptr;
108: long offset;
109:
110: long blockcount;
111: int collation;
112:
113: int btype;
114: char bt_desc[40];
115: long free_offset;
116:
117: long keyct;
118: ge_key *key_head;
119:
120: } ge_blockinfo;
121:
122:
123: typedef struct ge_buf {
124: char name[256];
125: char namespace[256];
126: char pth[4096];
127:
128: short fd;
129:
130: long blockct;
131: long gblsize;
132:
133: int blocknum;
134: char block_r[BLOCKLEN];
135: ge_blockinfo block;
136:
137: short block_dirty;
138: short is_free;
139: } ge_buf;
140:
141:
142: ge_buf ge_buffers[GE_MXGBL];
143:
144: int ge_curbuf;
145: int ge_nextbuf;
146:
147: int ge_rows;
148: int ge_columns;
149:
150: void ge_update_gbl(void);
151: void ge_select_buffer(int buf);
152: short ge_select_block(int buf, long blocknum);
153: void ge_update_block(void);
154: int ge_open_global(char *gblname);
155: void ge_init_buffers(void);
156: void ge_eventloop(void);
157: void ge_decode_key(const char *key, char *buf);
158: void ge_describe_block(void);
159:
160: #define SPC ' '
161:
162:
163: void ge_describe_gbl(void)
164: {
165: printf ("global %s [#%d]\n", ge_buffers[ge_curbuf].name, ge_curbuf);
166: printf ("namespace %s\n", ge_buffers[ge_curbuf].namespace);
167: printf ("block size %d bytes\n", BLOCKLEN);
168: printf ("block count %ld\n", ge_buffers[ge_curbuf].blockct);
169: printf ("file size %ld bytes\n", ge_buffers[ge_curbuf].gblsize);
170: }
171:
172: void ge_update_gbl(void)
173: {
174: printf ("fmadm: selected global %s into buffer %d\n", ge_buffers[ge_curbuf].name, ge_curbuf);
175: }
176:
177:
178: int fma_globals_edit(int optc, char **opts)
179: {
180: #define MODE_GBL 0
181: #define MODE_BLK 1
182: #if defined(HAVE_LIBREADLINE) && !defined(_AIX)
183: int editbuf;
184: char fmge_prompt[256];
185: char *cmdt = (char *) malloc (65535 * sizeof (char));
186: char *fmarl_buf;
187: char **args;
188: int mode = MODE_GBL;
189: char *result = (char *) malloc (65535 * sizeof (char));;
190: /* int argc;*/
191: register int i;
192:
193: /* first dimension */
194: if ((args = (char **) malloc (FMA_MAXARGS * sizeof (char *))) == NULL) {
195: fprintf (stderr, "fmadm [FATAL]: could not acquire memory\n");
196: return 1;
197: }
198:
199: /* second dimension */
200: for (i = 0; i < FMA_MAXARGS; i++) {
201: if ((args[i] = (char *) malloc (STRLEN * sizeof (char *))) == NULL) {
202: fprintf (stderr, "fmadm [FATAL]: could not acquire memory\n");
203: return 1;
204: }
205: }
206:
207:
208: ge_curbuf = 0;
209: ge_nextbuf = 0;
210: ge_init_buffers ();
211:
212: if (optc == 2) {
213:
214: if ((editbuf = ge_open_global (opts[fma_base_opt])) == -1) {
215: fprintf (stderr, "fmadm: cannot open global %s\n", opts[fma_base_opt]);
216: }
217: else {
218: ge_select_buffer (editbuf);
219: }
220:
221: }
222:
223: while(1) {
224: if (mode == MODE_GBL) {
225: snprintf (fmge_prompt, sizeof (fmge_prompt), "global edit %s [buf %d/%d]> ",
226: ge_buffers[ge_curbuf].name,
227: ge_curbuf,
228: GE_MXGBL);
229: }
230: else {
231: snprintf (fmge_prompt, sizeof (fmge_prompt), "block edit %s [blk %d/%ld]> ",
232: ge_buffers[ge_curbuf].name,
233: ge_buffers[ge_curbuf].blocknum,
234: ge_buffers[ge_curbuf].blockct);
235: }
236:
237: fmarl_buf = readline (fmge_prompt);
238: if (fmarl_buf == (char *) NULL) continue;
239:
240: cmdt = strtok (fmarl_buf, " ");
241: if (cmdt == (char *) NULL) continue;
242:
243: i = 0;
244: while ((result = strtok (NULL, " ")) != NULL) {
245: strcpy (args[i++], result);
246: }
247:
248: for (i = 0; i < strlen (cmdt); i++) cmdt[i] = cmdt[i] | 0140;
249:
250: if (strcmp (cmdt, "exit") == 0 || strcmp (cmdt, "quit") == 0) {
251: return 0;
252: }
253: else if (strcmp (cmdt, "block") == 0) {
254: mode = MODE_BLK;
255: }
256: else if (strcmp (cmdt, "global") == 0) {
257: mode = MODE_GBL;
258: }
259: else if (strcmp (cmdt, "describe") == 0) {
260: if (mode == MODE_GBL) {
261: ge_describe_gbl ();
262: }
263: else {
264: ge_describe_block ();
265: }
266: }
267: else if (strcmp (cmdt, "open") == 0) {
268: if (mode == MODE_GBL) {
269: printf ("TODO\n");
270: }
271: else {
272: long blknum;
273: blknum = atoi (args[0]);
274: ge_select_block (ge_curbuf, blknum);
275: }
276: }
277: else {
278: printf ("fmadm: command %s not recognized\n", cmdt);
279: }
280:
281:
282: }
283: #endif
284:
285:
286: return 0;
287: }
288:
289:
290: void ge_init_buffers(void)
291: {
292: register int i;
293: register int j;
294:
295: for (i = 0; i < GE_MXGBL; i++) {
296: ge_buffers[i].name[0] = '\0';
297: ge_buffers[i].namespace[0] = '\0';
298: ge_buffers[i].pth[0] = '\0';
299: ge_buffers[i].fd = 0;
300: ge_buffers[i].blocknum = 0;
301: ge_buffers[i].block_dirty = FALSE;
302: ge_buffers[i].is_free = TRUE;
303: ge_buffers[i].blockct = 0;
304: ge_buffers[i].gblsize = 0;
305:
306: for (j = 0; j < BLOCKLEN; j++) {
307: ge_buffers[i].block_r[j] = '\0';
308: }
309: }
310: }
311:
312:
313:
314:
315: void ge_select_buffer(int buf)
316: {
317: printf ("fmadm: selected buffer %d\n", buf);
318:
319: ge_curbuf = buf;
320:
321: ge_update_gbl ();
322: }
323:
324: short ge_select_block(int buf, long blocknum)
325: {
326: ge_blockinfo *b;
327: char *br;
328: char key[2056];
329: char decoded[64096];
330: long i;
331: long j;
332: long k;
333: long length;
334:
335: if ((blocknum < 0) || (blocknum > (ge_buffers[buf].blockct - 1))) {
336: printf ("block number for global %s must be between 0 and %ld\n", ge_buffers[buf].name, ge_buffers[buf].blockct - 1);
337: return FALSE;
338: }
339:
340: b = &(ge_buffers[buf].block);
341: br = ge_buffers[buf].block_r;
342:
343: lseek (ge_buffers[buf].fd, blocknum * BLOCKLEN, 0);
344: read (ge_buffers[buf].fd, br, BLOCKLEN);
345:
346: ge_buffers[buf].blocknum = blocknum;
347: ge_buffers[buf].block_dirty = FALSE;
348:
349: b->btype = br[BTYP];
350: switch (b->btype) {
351:
352: case DATA:
353: snprintf (b->bt_desc, sizeof (b->bt_desc) - 1, "DATA");
354: break;
355:
356: case POINTER:
357: snprintf (b->bt_desc, sizeof (b->bt_desc) - 1, "POINTER");
358: break;
359:
360: case BOTTOM:
361: snprintf (b->bt_desc, sizeof (b->bt_desc) - 1, "BTM PTR");
362: break;
363:
364: case EMPTY:
365: snprintf (b->bt_desc, sizeof (b->bt_desc) - 1, "EMPTY");
366: break;
367:
368: case FBLK:
369: snprintf (b->bt_desc, sizeof (b->bt_desc) - 1, "FBLK");
370: break;
371:
372: default:
373: snprintf (b->bt_desc, sizeof (b->bt_desc) - 1, "ILLEGAL TYPE");
374: break;
375:
376: }
377:
378: if (blocknum == ROOT) strcat (b->bt_desc, " [ROOT]");
379:
380: if (blocknum != ROOT) {
381: b->llptr = UNSIGN (br[LLPTR]) * 65536 + UNSIGN (br[LLPTR + 1]) * 256 + UNSIGN(br[LLPTR + 2]);
382: b->rlptr = UNSIGN (br[RLPTR]) * 65536 + UNSIGN (br[RLPTR + 1]) * 256 + UNSIGN(br[RLPTR + 2]);
383: }
384: else {
385: b->blockcount = UNSIGN (br[LLPTR]) * 65536 + UNSIGN (br[LLPTR + 1]) * 256 + UNSIGN(br[LLPTR + 2]);
386: b->free_offset = UNSIGN (br[RLPTR]) * 65536 + UNSIGN (br[RLPTR + 1]) * 256 + UNSIGN(br[RLPTR + 2]);
387: }
388:
389: b->offset = UNSIGN (br[OFFS]) * 256 + UNSIGN (br[OFFS + 1]);
390: b->keyct = 0;
391:
392: if (b->btype == FBLK) goto skip_keydec;
393:
394: i = 0;
395: while (i < b->offset) {
396:
397: length = UNSIGN (br[i++]);
398: k = UNSIGN (br[i++]);
399:
400: if ((i + length) > b->offset) break;
401:
402: for (j = 0; j < length; j++) {
403: key[k++] = br[i++];
404: }
405:
406: key[k] = g_EOL;
407:
408: ge_decode_key (key, decoded);
409:
410: printf ("found key %s\n", decoded);
411:
412: b->keyct++;
413: }
414:
415: skip_keydec:
416:
417: ge_update_block ();
418:
419: return TRUE;
420: }
421:
422: void ge_decode_key(const char *key, char *buf)
423: {
424: int ch;
425: short ch0;
426: short i;
427: short j;
428: short k;
429: short typ;
430:
431: j = 0;
432: i = 0;
433: k = 1;
434:
435: buf[j++] = '(';
436:
437: while ((ch = UNSIGN (key[i++])) != g_EOL) {
438:
439: if (k) {
440:
441: k = 0;
442:
443: if ((typ = (ch > SPC))) {
444: buf[j++] = '"';
445: }
446:
447: }
448:
449: ch0 = (ch >= SPC ? (ch >> 1) : (ch < 20 ? (ch >> 1) + '0' : (ch >> 1) + SPC));
450:
451: if (ch0 == DEL) {
452: if (((ch = UNSIGN (key[i++])) >> 1) == DEL) {
453: ch0 += DEL;
454: ch = UNSIGN (key[i++]);
455: }
456:
457: ch0 += (ch >> 1);
458: buf[j] = '<';
459: buf[++j] = '0' + ch0 / 100;
460: buf[++j] = '0' + (ch0 % 100) / 10;
461: buf[++j] = '0' + ch0 % 10;
462: buf[++j] = '>';
463: }
464: else {
465: buf[j] = ch0;
466: }
467:
468: if (buf[j++] == '"') {
469: buf[j++] = '"';
470: }
471:
472: if (ch & 01) {
473: if (typ) buf[j++] = '"';
474: buf[j++] = ',';
475: k = 1;
476: }
477: }
478:
479: buf[j--] = 0;
480: buf[j] = ')';
481: if (j == 0) buf[0] = 0;
482:
483: while (j >= 0) {
484: if ((ch = buf[--j]) < SPC || ch >= DEL) break;
485: }
486:
487: }
488:
489: void ge_describe_block(void)
490: {
491: printf ("type = %s\n", ge_buffers[ge_curbuf].block.bt_desc);
492:
493: if (ge_buffers[ge_curbuf].blocknum != ROOT) {
494: printf ("llptr = %ld\n", ge_buffers[ge_curbuf].block.llptr);
495: printf ("rlptr = %ld\n", ge_buffers[ge_curbuf].block.rlptr);
496: }
497: else {
498: printf ("block count = %ld\n", ge_buffers[ge_curbuf].block.blockcount);
499: printf ("offset to free space = %ld\n", ge_buffers[ge_curbuf].block.free_offset);
500: }
501:
502: printf ("key count = %ld\n", ge_buffers[ge_curbuf].block.keyct);
503: }
504:
505: void ge_update_block(void)
506: {
507:
508: printf ("fmadm: selected block %d of %ld\n", ge_buffers[ge_curbuf].blocknum, ge_buffers[ge_curbuf].blockct);
509:
510: }
511:
512: int ge_open_global(char *gblname)
513: {
514: char gpath[4096];
515: int buf;
516: struct stat sb;
517:
518: buf = ge_nextbuf++;
519:
520: snprintf (gpath, sizeof (gpath) - 1, "%s/%s", fma_global_path, gblname);
521:
522: printf ("fmadm: opening global %s [path %s, namespace %s]... ", gblname, gpath, fma_namespace);
523:
524: if ((ge_buffers[buf].fd = open (gpath, 0)) == -1) {
525: printf ("[FAIL]\n");
526: return -1;
527: }
528: else {
529: printf ("[OK]\n");
530: }
531:
532: fstat (ge_buffers[buf].fd, &sb);
533:
534: ge_buffers[buf].gblsize = sb.st_size;
535: ge_buffers[buf].blockct = sb.st_size / BLOCKLEN;
536:
537: strcpy (ge_buffers[buf].name, gblname);
538: strcpy (ge_buffers[buf].namespace, fma_namespace);
539: strcpy (ge_buffers[buf].pth, gpath);
540:
541: ge_buffers[buf].blocknum = 0;
542: ge_buffers[buf].block_dirty = FALSE;
543:
544: ge_curbuf = buf;
545:
546: ge_select_block (buf, 0);
547:
548: return buf;
549: }
550:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>