/*
* $Id: fma_gedit.c,v 1.5 2025/03/31 16:33:56 snw Exp $
* FreeM global editor
*
*
* Author: Serena Willis <snw@coherent-logic.com>
* Copyright (C) 1998 MUG Deutschland
* Copyright (C) 2023, 2025 Coherent Logic Development LLC
*
*
* This file is part of FreeM.
*
* FreeM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FreeM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero Public License for more details.
*
* You should have received a copy of the GNU Affero Public License
* along with FreeM. If not, see <https://www.gnu.org/licenses/>.
*
* $Log: fma_gedit.c,v $
* Revision 1.5 2025/03/31 16:33:56 snw
* Work on fmadm edit global
*
* Revision 1.4 2025/03/30 01:36:58 snw
* Make it easier to bring back fma_gedit, fix double-free in global handler, limit $CHAR to 7-bit ASCII
*
* Revision 1.3 2025/03/09 19:14:24 snw
* First phase of REUSE compliance and header reformat
*
*
* SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
* SPDX-License-Identifier: AGPL-3.0-or-later
**/
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "fmadm.h"
#include "fma_globals.h"
#ifdef HAVE_LIBREADLINE
# if defined(HAVE_READLINE_READLINE_H)
# include <readline/readline.h>
# elif defined(HAVE_READLINE_H)
# include <readline.h>
# else /* !defined(HAVE_READLINE_H) */
extern char *readline ();
# endif /* !defined(HAVE_READLINE_H) */
/*char *cmdline = NULL;*/
#else /* !defined(HAVE_READLINE_READLINE_H) */
/* no readline */
#endif /* HAVE_LIBREADLINE */
#ifdef HAVE_READLINE_HISTORY
# if defined(HAVE_READLINE_HISTORY_H)
# include <readline/history.h>
# elif defined(HAVE_HISTORY_H)
# include <history.h>
# else /* !defined(HAVE_HISTORY_H) */
extern void add_history ();
extern int write_history ();
extern int read_history ();
# endif /* defined(HAVE_READLINE_HISTORY_H) */
/* no history */
#endif /* HAVE_READLINE_HISTORY */
#define GE_MXGBL 100
typedef struct ge_key {
char key[256];
char data[256];
struct ge_key *next;
} ge_key;
typedef struct ge_blockinfo {
int keylen;
int keyoffs;
char key[STRLEN];
int datalen;
char data[STRLEN];
long llptr;
long rlptr;
long offset;
long blockcount;
int collation;
int btype;
char bt_desc[40];
long free_offset;
long keyct;
ge_key *key_head;
} ge_blockinfo;
typedef struct ge_buf {
char name[256];
char namespace[256];
char pth[4096];
short fd;
long blockct;
long gblsize;
int blocknum;
char block_r[BLOCKLEN];
ge_blockinfo block;
short block_dirty;
short is_free;
} ge_buf;
ge_buf ge_buffers[GE_MXGBL];
int ge_curbuf;
int ge_nextbuf;
int ge_rows;
int ge_columns;
void ge_update_gbl(void);
void ge_select_buffer(int buf);
short ge_select_block(int buf, long blocknum);
void ge_update_block(void);
int ge_open_global(char *gblname);
void ge_init_buffers(void);
void ge_eventloop(void);
void ge_decode_key(const char *key, char *buf);
void ge_describe_block(void);
#define SPC ' '
void ge_describe_gbl(void)
{
printf ("global %s [#%d]\n", ge_buffers[ge_curbuf].name, ge_curbuf);
printf ("namespace %s\n", ge_buffers[ge_curbuf].namespace);
printf ("block size %d bytes\n", BLOCKLEN);
printf ("block count %d\n", ge_buffers[ge_curbuf].blockct);
printf ("file size %d bytes\n", ge_buffers[ge_curbuf].gblsize);
}
void ge_update_gbl(void)
{
printf ("fmadm: selected global %s into buffer %d\n", ge_buffers[ge_curbuf].name, ge_curbuf);
}
int fma_globals_edit(int optc, char **opts)
{
#define MODE_GBL 0
#define MODE_BLK 1
#if defined(HAVE_LIBREADLINE) && !defined(_AIX)
int editbuf;
char fmge_prompt[256];
char *cmdt = (char *) malloc (65535 * sizeof (char));
char *fmarl_buf;
char **args;
int mode = MODE_GBL;
char *result = (char *) malloc (65535 * sizeof (char));;
int argc;
int tmpres;
register int i;
/* first dimension */
if ((args = (char **) malloc (FMA_MAXARGS * sizeof (char *))) == NULL) {
fprintf (stderr, "fmadm [FATAL]: could not acquire memory\n");
return 1;
}
/* second dimension */
for (i = 0; i < FMA_MAXARGS; i++) {
if ((args[i] = (char *) malloc (STRLEN * sizeof (char *))) == NULL) {
fprintf (stderr, "fmadm [FATAL]: could not acquire memory\n");
return 1;
}
}
ge_curbuf = 0;
ge_nextbuf = 0;
ge_init_buffers ();
if (optc == 2) {
if ((editbuf = ge_open_global (opts[fma_base_opt])) == -1) {
fprintf (stderr, "fmadm: cannot open global %s\n", opts[fma_base_opt]);
}
else {
ge_select_buffer (editbuf);
}
}
while(1) {
if (mode == MODE_GBL) {
sprintf (fmge_prompt, "global edit %s [buf %d/%d]> ",
ge_buffers[ge_curbuf].name,
ge_curbuf,
GE_MXGBL);
}
else {
sprintf (fmge_prompt, "block edit %s [blk %d/%d]> ",
ge_buffers[ge_curbuf].name,
ge_buffers[ge_curbuf].blocknum,
ge_buffers[ge_curbuf].blockct);
}
fmarl_buf = readline (fmge_prompt);
if (fmarl_buf == (char *) NULL) continue;
cmdt = strtok (fmarl_buf, " ");
if (cmdt == (char *) NULL) continue;
i = 0;
while ((result = strtok (NULL, " ")) != NULL) {
strcpy (args[i++], result);
}
argc = i;
for (i = 0; i < strlen (cmdt); i++) cmdt[i] = cmdt[i] | 0140;
if (strcmp (cmdt, "exit") == 0 || strcmp (cmdt, "quit") == 0) {
return 0;
}
else if (strcmp (cmdt, "block") == 0) {
mode = MODE_BLK;
}
else if (strcmp (cmdt, "global") == 0) {
mode = MODE_GBL;
}
else if (strcmp (cmdt, "describe") == 0) {
if (mode == MODE_GBL) {
ge_describe_gbl ();
}
else {
ge_describe_block ();
}
}
else if (strcmp (cmdt, "open") == 0) {
if (mode == MODE_GBL) {
printf ("TODO\n");
}
else {
long blknum;
blknum = atoi (args[0]);
ge_select_block (ge_curbuf, blknum);
}
}
else {
printf ("fmadm: command %s not recognized\n", cmdt);
}
}
#endif
return 0;
}
void ge_init_buffers(void)
{
register int i;
register int j;
for (i = 0; i < GE_MXGBL; i++) {
ge_buffers[i].name[0] = '\0';
ge_buffers[i].namespace[0] = '\0';
ge_buffers[i].pth[0] = '\0';
ge_buffers[i].fd = 0;
ge_buffers[i].blocknum = 0;
ge_buffers[i].block_dirty = FALSE;
ge_buffers[i].is_free = TRUE;
ge_buffers[i].blockct = 0;
ge_buffers[i].gblsize = 0;
for (j = 0; j < BLOCKLEN; j++) {
ge_buffers[i].block_r[j] = '\0';
}
}
}
void ge_select_buffer(int buf)
{
printf ("fmadm: selected buffer %d\n", buf);
ge_curbuf = buf;
ge_update_gbl ();
}
short ge_select_block(int buf, long blocknum)
{
ge_blockinfo *b;
char *br;
char key[2056];
char decoded[64096];
long i;
long j;
long k;
long length;
if ((blocknum < 0) || (blocknum > (ge_buffers[buf].blockct - 1))) {
printf ("block number for global %s must be between 0 and %d\n", ge_buffers[buf].name, ge_buffers[buf].blockct - 1);
return FALSE;
}
b = &(ge_buffers[buf].block);
br = ge_buffers[buf].block_r;
lseek (ge_buffers[buf].fd, blocknum * BLOCKLEN, 0);
read (ge_buffers[buf].fd, br, BLOCKLEN);
ge_buffers[buf].blocknum = blocknum;
ge_buffers[buf].block_dirty = FALSE;
b->btype = br[BTYP];
switch (b->btype) {
case DATA:
snprintf (b->bt_desc, 39, "DATA");
break;
case POINTER:
snprintf (b->bt_desc, 39, "POINTER");
break;
case BOTTOM:
snprintf (b->bt_desc, 39, "BTM PTR");
break;
case EMPTY:
snprintf (b->bt_desc, 39, "EMPTY");
break;
case FBLK:
snprintf (b->bt_desc, 39, "FBLK");
break;
default:
snprintf (b->bt_desc, 39, "ILLEGAL TYPE");
break;
}
if (blocknum == ROOT) strcat (b->bt_desc, " [ROOT]");
if (blocknum != ROOT) {
b->llptr = UNSIGN (br[LLPTR]) * 65536 + UNSIGN (br[LLPTR + 1]) * 256 + UNSIGN(br[LLPTR + 2]);
b->rlptr = UNSIGN (br[RLPTR]) * 65536 + UNSIGN (br[RLPTR + 1]) * 256 + UNSIGN(br[RLPTR + 2]);
}
else {
b->blockcount = UNSIGN (br[LLPTR]) * 65536 + UNSIGN (br[LLPTR + 1]) * 256 + UNSIGN(br[LLPTR + 2]);
b->free_offset = UNSIGN (br[RLPTR]) * 65536 + UNSIGN (br[RLPTR + 1]) * 256 + UNSIGN(br[RLPTR + 2]);
}
b->offset = UNSIGN (br[OFFS]) * 256 + UNSIGN (br[OFFS + 1]);
b->keyct = 0;
if (b->btype == FBLK) goto skip_keydec;
i = 0;
while (i < b->offset) {
length = UNSIGN (br[i++]);
k = UNSIGN (br[i++]);
if ((i + length) > b->offset) break;
for (j = 0; j < length; j++) {
key[k++] = br[i++];
}
key[k] = g_EOL;
ge_decode_key (key, decoded);
printf ("found key %s\n", decoded);
b->keyct++;
}
skip_keydec:
ge_update_block ();
return TRUE;
}
void ge_decode_key(const char *key, char *buf)
{
int ch;
short ch0;
short i;
short j;
short k;
short typ;
j = 0;
i = 0;
k = 1;
buf[j++] = '(';
while ((ch = UNSIGN (key[i++])) != g_EOL) {
if (k) {
k = 0;
if ((typ = (ch > SPC))) {
buf[j++] = '"';
}
}
ch0 = (ch >= SPC ? (ch >> 1) : (ch < 20 ? (ch >> 1) + '0' : (ch >> 1) + SPC));
if (ch0 == DEL) {
if (((ch = UNSIGN (key[i++])) >> 1) == DEL) {
ch0 += DEL;
ch = UNSIGN (key[i++]);
}
ch0 += (ch >> 1);
buf[j] = '<';
buf[++j] = '0' + ch0 / 100;
buf[++j] = '0' + (ch0 % 100) / 10;
buf[++j] = '0' + ch0 % 10;
buf[++j] = '>';
}
else {
buf[j] = ch0;
}
if (buf[j++] == '"') {
buf[j++] = '"';
}
if (ch & 01) {
if (typ) buf[j++] = '"';
buf[j++] = ',';
k = 1;
}
}
buf[j--] = 0;
buf[j] = ')';
if (j == 0) buf[0] = 0;
while (j >= 0) {
if ((ch = buf[--j]) < SPC || ch >= DEL) break;
}
}
void ge_describe_block(void)
{
printf ("type = %s\n", ge_buffers[ge_curbuf].block.bt_desc);
if (ge_buffers[ge_curbuf].blocknum != ROOT) {
printf ("llptr = %d\n", ge_buffers[ge_curbuf].block.llptr);
printf ("rlptr = %d\n", ge_buffers[ge_curbuf].block.rlptr);
}
else {
printf ("block count = %d\n", ge_buffers[ge_curbuf].block.blockcount);
printf ("offset to free space = %d\n", ge_buffers[ge_curbuf].block.free_offset);
}
printf ("key count = %d\n", ge_buffers[ge_curbuf].block.keyct);
}
void ge_update_block(void)
{
printf ("fmadm: selected block %d of %d\n", ge_buffers[ge_curbuf].blocknum, ge_buffers[ge_curbuf].blockct);
}
int ge_open_global(char *gblname)
{
char gpath[4096];
int buf;
struct stat sb;
buf = ge_nextbuf++;
snprintf (gpath, 4095, "%s/%s", fma_global_path, gblname);
printf ("fmadm: opening global %s [path %s, namespace %s]... ", gblname, gpath, fma_namespace);
if ((ge_buffers[buf].fd = open (gpath, 0)) == -1) {
printf ("[FAIL]\n");
return -1;
}
else {
printf ("[OK]\n");
}
fstat (ge_buffers[buf].fd, &sb);
ge_buffers[buf].gblsize = sb.st_size;
ge_buffers[buf].blockct = sb.st_size / BLOCKLEN;
strcpy (ge_buffers[buf].name, gblname);
strcpy (ge_buffers[buf].namespace, fma_namespace);
strcpy (ge_buffers[buf].pth, gpath);
ge_buffers[buf].blocknum = 0;
ge_buffers[buf].block_dirty = FALSE;
ge_curbuf = buf;
ge_select_block (buf, 0);
return buf;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>