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