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