--- freem/src/fmadm.c	2025/01/19 02:04:04	1.1
+++ freem/src/fmadm.c	2025/04/01 14:32:11	1.19
@@ -1,23 +1,11 @@
 /*
- *                            *
- *                           * *
- *                          *   *
- *                     ***************
- *                      * *       * *
- *                       *  MUMPS  *
- *                      * *       * *
- *                     ***************
- *                          *   *
- *                           * *
- *                            *
- *
- *   fmadm.c
+ *   $Id: fmadm.c,v 1.19 2025/04/01 14:32:11 snw Exp $
  *    FreeM Administration Tool
  *
  *  
- *   Author: Serena Willis <jpw@coherent-logic.com>
+ *   Author: Serena Willis <snw@coherent-logic.com>
  *    Copyright (C) 1998 MUG Deutschland
- *    Copyright (C) 2020, 2023 Coherent Logic Development LLC
+ *    Copyright (C) 2020, 2023, 2025 Coherent Logic Development LLC
  *
  *
  *   This file is part of FreeM.
@@ -35,10 +23,55 @@
  *   You should have received a copy of the GNU Affero Public License
  *   along with FreeM.  If not, see <https://www.gnu.org/licenses/>.
  *
+ *   $Log: fmadm.c,v $
+ *   Revision 1.19  2025/04/01 14:32:11  snw
+ *   Begin work on environment and namespace reorg
+ *
+ *   Revision 1.18  2025/03/31 16:33:56  snw
+ *   Work on fmadm edit global
+ *
+ *   Revision 1.17  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.16  2025/03/24 20:59:58  snw
+ *   Try using DosCopy API instead of built-in cp function on OS/2
+ *
+ *   Revision 1.15  2025/03/24 20:58:05  snw
+ *   Try using DosCopy API instead of built-in cp function on OS/2
+ *
+ *   Revision 1.14  2025/03/24 20:57:06  snw
+ *   Try using DosCopy API instead of built-in cp function on OS/2
+ *
+ *   Revision 1.13  2025/03/24 20:15:09  snw
+ *   Set file permissions on freemd.exe on OS/2 in fmadm configure
+ *
+ *   Revision 1.12  2025/03/24 20:13:34  snw
+ *   Set file permissions on freemd.exe on OS/2 in fmadm configure
+ *
+ *   Revision 1.11  2025/03/24 19:25:48  snw
+ *   Make fmadm configure copy freem.exe to freemd.exe for daemon operation on OS/2 systems
+ *
+ *   Revision 1.10  2025/03/24 19:22:16  snw
+ *   Make fmadm configure copy freem.exe to freemd.exe for daemon operation on OS/2 systems
+ *
+ *   Revision 1.9  2025/03/24 19:19:42  snw
+ *   Make fmadm configure copy freem.exe to freemd.exe for daemon operation on OS/2 systems
+ *
+ *   Revision 1.8  2025/03/22 18:43:54  snw
+ *   Make STRLEN 255 chars and add BIGSTR macro for larger buffers
+ *
+ *   Revision 1.7  2025/03/09 19:14:25  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 <sys/types.h>
 #include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <string.h>
@@ -46,11 +79,16 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <errno.h>
+#include <ctype.h>
 #include "config.h"
 #include "transact.h"
 #include "namespace.h"
 #include "fs.h"
 
+#if defined(__OS2__)
+# include <os2.h>
+#endif
+
 #ifdef HAVE_LIBREADLINE
 #  if defined(HAVE_READLINE_READLINE_H)
 #    include <readline/readline.h>
@@ -90,13 +128,13 @@ extern int read_history ();
 /* namespace configuration */
 char fma_environment[STRLEN];
 char fma_namespace[STRLEN];
-char fma_routine_path[STRLEN];
-char fma_global_path[STRLEN];
-char fma_journal_path[STRLEN];
-char fma_pct_global_path[STRLEN];
-char fma_pct_routine_path[STRLEN];
+char fma_routine_path[PATHLEN];
+char fma_global_path[PATHLEN];
+char fma_journal_path[PATHLEN];
+char fma_pct_global_path[PATHLEN];
+char fma_pct_routine_path[PATHLEN];
 char fma_journal_cut_threshold[STRLEN];
-char fma_locktab[STRLEN];
+char fma_locktab[PATHLEN];
 short fma_base_opt = 1;
 short fma_min_args = 2;
 short fma_explicit_namespace = FALSE;
@@ -554,6 +592,8 @@ int fm_shell (void)
         
         cmdt = strtok (fmarl_buf, " ");
 
+        if (cmdt == (char *) NULL) continue;
+        
         for (i = 0; i < strlen (cmdt); i++) cmdt[i] = cmdt[i] | 0140;        
         
         if (strcmp (cmdt, "exit") == 0) cmd = FMAC_EXIT;
@@ -757,7 +797,7 @@ int fm_shell (void)
                 printf ("Global Path:                %s\n", fma_global_path);
                 printf ("%%-Global Path:              %s\n", fma_pct_global_path);
                 printf ("Journal File:               %s\n", fma_journal_path);
-                printf ("Journal Cut Threshold:      %ld bytes\n", fma_journal_cut_threshold);
+                printf ("Journal Cut Threshold:      %s bytes\n", fma_journal_cut_threshold);
                 break;
 
             case FMAC_EXIT:
@@ -1025,7 +1065,7 @@ int fm_edit (short object, int optc, cha
 
         case OBJ_GLOBAL:
             return fma_globals_edit (optc, options);
-            
+                        
         default:
             fprintf (stderr, "fmadm:  'edit' is an invalid action for '%s'\n", obj_str);
             return 1;
@@ -1060,7 +1100,7 @@ void fm_reconfigure(void)
     retval = rename (config_file, config_backup);
 
     if (retval == 0) {
-	fprintf (stderr, "[OK]\n\n");
+	fprintf (stderr, "[OK]\n");
 	
 	fm_configure ();
 
@@ -1077,7 +1117,7 @@ void fm_reconfigure(void)
 
 void fm_configure (void)
 {
-
+    char varbase[4096];
     char sysrtn[4096];
     char sysgbl[4096];
     char usrrtn[4096];
@@ -1101,7 +1141,19 @@ void fm_configure (void)
 
     struct stat etcstat;
     int stat_result;
+
+    DIR *dir;
+    struct dirent *ent;
+    char src_dir[4096];
+    char dest_dir[4096];
+
+    struct group *d_grp;
+    struct passwd *d_user;
+    gid_t d_gid;
+    uid_t d_uid;   
+
     
+    snprintf (varbase, 4095, "%s/freem", LOCALSTATEDIR);
     snprintf (sysrtn, 4095, "%s/freem/%s/SYSTEM/routines", LOCALSTATEDIR, fma_environment);
     snprintf (sysgbl, 4095, "%s/freem/%s/SYSTEM/globals", LOCALSTATEDIR, fma_environment);
     snprintf (usrrtn, 4095, "%s/freem/%s/USER/routines", LOCALSTATEDIR, fma_environment);
@@ -1117,11 +1169,25 @@ void fm_configure (void)
         fprintf (stderr, "fmadm:  not superuser\n");
         exit (1);
     }
+
+#if !defined(__OS2__)
+    if ((d_grp = getgrnam ("freem")) == NULL) {
+        fprintf (stderr, "fmadm:  'freem' group must exist before configuring\n");
+        exit (1);
+    }
+    d_gid = d_grp->gr_gid;
+
+    if ((d_user = getpwnam ("freem")) == NULL) {
+        fprintf (stderr, "fmadm:  'freem' user must exist before configuring\n");
+        exit (1);
+    }
+    d_uid = d_user->pw_uid;
+#endif
     
     if (file_exists (config_file)) {
-        fprintf (stderr, "fmadm:  '%s' already exists.\n\n", config_file);
-        fprintf (stderr, "'fmadm configure' may only be used on a fresh installation of FreeM.\n");
-        exit (1);
+        fprintf (stderr, "fmadm:  '%s' already exists; running fmadm reconfigure instead\n", config_file);
+        fm_reconfigure ();
+        return;
     }
 
 
@@ -1134,99 +1200,192 @@ void fm_configure (void)
     snprintf (envbase, 4095, "%s/freem/%s", SYSCONFDIR, fma_environment); 
     snprintf (nsbase, 4095, "%s/freem/%s", LOCALSTATEDIR, fma_environment);
 
+#if defined(__OS2__)
+    {
+        char srcfile[PATHLEN];
+        char dstfile[PATHLEN];        
+        
+        snprintf (srcfile, PATHLEN, "%s/bin/freem.exe", PREFIX);
+        snprintf (dstfile, PATHLEN, "%s/bin/freemd.exe", PREFIX);
+
+        unlink (dstfile);
+        
+        fprintf (stderr, "fmadm:  running on OS/2; will copy %s to %s\n", srcfile, dstfile);
+        
+        if (DosCopy (srcfile, dstfile, 1) != 0) {
+            fprintf (stderr, "fmadm:  fatal error copying %s to %s\n", srcfile, dstfile);
+            exit (1);
+        }
+
+        chmod (dstfile, 0755);
+    }
+#else
+    fprintf (stderr, "fmadm:  not running on OS/2\n");
+#endif
+    
     printf ("\nFreeM Initial Configuration\n");
     printf ("---------------------------\n\n");
 
     printf ("This utility will create the initial configuration file for ");
     printf ("FreeM environment '%s' in %s.\n\n", fma_environment, config_file);    
-
     
     /* check for existence of needed directories */
     if (stat (SYSCONFDIR, &etcstat) == -1) {
 	fprintf (stderr, "fmadm:  creating %s\n", SYSCONFDIR);
-	mkdir (SYSCONFDIR, 0755);
+	mkdir (SYSCONFDIR, 0775);
+#if !defined(__OS2__)
+        if (chown (SYSCONFDIR, d_uid, d_gid) != 0) {
+            fprintf (stderr, "fmadm:  error setting ownership on %s\n", SYSCONFDIR);
+            exit (1);
+        }
+#endif        
     }
 
     if (stat (confbase, &etcstat) == -1) {
         fprintf (stderr, "fmadm:  creating %s\n", confbase);
-        mkdir (confbase, 0755);
+        mkdir (confbase, 0775);
+#if !defined(__OS2__)
+        if (chown (confbase, d_uid, d_gid) != 0) {
+            fprintf (stderr, "fmadm:  error setting ownership on %s\n", confbase);
+            exit (1);
+        }
+#endif        
     }
 
     if (stat (envbase, &etcstat) == -1) {
         fprintf (stderr, "fmadm:  creating %s\n", envbase);
-        mkdir (envbase, 0755);
+        mkdir (envbase, 0775);
+#if !defined(__OS2__)
+        if (chown (envbase, d_uid, d_gid) != 0) {
+            fprintf (stderr, "fmadm:  error setting ownership on %s\n", envbase);
+            exit (1);
+        }
+#endif        
+        
     }
 
+    if (stat (varbase, &etcstat) == -1) {
+        fprintf (stderr, "fmadm:  creating %s\n", varbase);
+        mkdir (varbase, 0775);
+#if !defined(__OS2__)
+        if (chown (varbase, d_uid, d_gid) != 0) {
+            fprintf (stderr, "fmadm:  error setting ownership on %s\n", varbase);
+            exit (1);
+        }
+#endif        
+        
+    }
+    
     if (stat (nsbase, &etcstat) == -1) {
         fprintf (stderr, "fmadm:  creating %s\n", nsbase);
-        mkdir (nsbase, 0755);
-    }
-
-   
-
-    if (strcmp (fma_environment, "DEFAULT") != 0) {
-
-        DIR *dir;
-        struct dirent *ent;
-        char src_dir[4096];
-        char dest_dir[4096];
-
-        snprintf (src_dir, 4095, "%s/freem/DEFAULT/SYSTEM/routines", LOCALSTATEDIR);
-        snprintf (dest_dir, 4095, "%s/freem/%s/SYSTEM/routines", LOCALSTATEDIR, fma_environment);
-        
-        fprintf (stderr, "fmadm:  populating new environment '%s'\n", fma_environment);
-        
-        snprintf (buf, 4095, "%s/freem/%s/SYSTEM", LOCALSTATEDIR, fma_environment);
-        mkdir (buf, 0755);
-
-        snprintf (buf, 4095, "%s/freem/%s/USER", LOCALSTATEDIR, fma_environment);
-        mkdir (buf, 0755);
-
-        snprintf (buf, 4095, "%s/freem/%s/SYSTEM/routines", LOCALSTATEDIR, fma_environment);
-        mkdir (buf, 0755);
-
-        snprintf (buf, 4095, "%s/freem/%s/USER/globals", LOCALSTATEDIR, fma_environment);
-        mkdir (buf, 0755);
-
-        snprintf (buf, 4095, "%s/freem/%s/SYSTEM/globals", LOCALSTATEDIR, fma_environment);
-        mkdir (buf, 0755);
-
-        snprintf (buf, 4095, "%s/freem/%s/USER/routines", LOCALSTATEDIR, fma_environment);
-        mkdir (buf, 0755);
-
-        fprintf (stderr, "fmadm:  copying routines from '%s' to '%s'...\n", src_dir, dest_dir);
-
-        if ((dir = opendir (src_dir)) == NULL) {
-            fprintf (stderr, "\nfmadm:  could not open source directory %s\n", src_dir);
+        mkdir (nsbase, 0775);
+#if !defined(__OS2__)
+        if (chown (nsbase, d_uid, d_gid) != 0) {
+            fprintf (stderr, "fmadm:  error setting ownership on %s\n", nsbase);
             exit (1);
         }
+#endif        
+        
+    }
 
-        while ((ent = readdir (dir)) != NULL) {
-            char infile[4096];
-            char outfile[4096];
-           
-            if ((strcmp (ent->d_name, ".") != 0) && (strcmp (ent->d_name, "..") != 0)) {
-
-                fprintf (stderr, "\t%s\n", ent->d_name);
-                
-                snprintf (infile, 4095, "%s/%s", src_dir, ent->d_name);
-                snprintf (outfile, 4095, "%s/%s", dest_dir, ent->d_name);
+    snprintf (src_dir, 4095, "%s/freem/mlib", DATADIR);
+    snprintf (dest_dir, 4095, "%s/freem/%s/SYSTEM/routines", LOCALSTATEDIR, fma_environment);
+    
+    fprintf (stderr, "fmadm:  populating new environment '%s'\n", fma_environment);
+    
+    snprintf (buf, 4095, "%s/freem/%s/SYSTEM", LOCALSTATEDIR, fma_environment);
+    mkdir (buf, 0775);
+#if !defined(__OS2__)
+    if (chown (buf, d_uid, d_gid) != 0) {
+        fprintf (stderr, "fmadm:  error setting ownership on %s\n", buf);
+        exit (1);
+    }
+#endif        
+    
+    
+    snprintf (buf, 4095, "%s/freem/%s/USER", LOCALSTATEDIR, fma_environment);
+    mkdir (buf, 0775);
+#if !defined(__OS2__)
+    if (chown (buf, d_uid, d_gid) != 0) {
+        fprintf (stderr, "fmadm:  error setting ownership on %s\n", buf);
+        exit (1);
+    }
+#endif        
+    
+    snprintf (buf, 4095, "%s/freem/%s/SYSTEM/routines", LOCALSTATEDIR, fma_environment);
+    mkdir (buf, 0775);
+#if !defined(__OS2__)
+    if (chown (buf, d_uid, d_gid) != 0) {
+        fprintf (stderr, "fmadm:  error setting ownership on %s\n", buf);
+        exit (1);
+    }
+#endif        
+    
+    snprintf (buf, 4095, "%s/freem/%s/USER/globals", LOCALSTATEDIR, fma_environment);
+    mkdir (buf, 0775);
+#if !defined(__OS2__)
+    if (chown (buf, d_uid, d_gid) != 0) {
+        fprintf (stderr, "fmadm:  error setting ownership on %s\n", buf);
+        exit (1);
+    }
+#endif        
+    
+    snprintf (buf, 4095, "%s/freem/%s/SYSTEM/globals", LOCALSTATEDIR, fma_environment);
+    mkdir (buf, 0775);
+#if !defined(__OS2__)
+    if (chown (buf, d_uid, d_gid) != 0) {
+        fprintf (stderr, "fmadm:  error setting ownership on %s\n", buf);
+        exit (1);
+    }
+#endif        
+    
+    snprintf (buf, 4095, "%s/freem/%s/USER/routines", LOCALSTATEDIR, fma_environment);
+    mkdir (buf, 0775);
+#if !defined(__OS2__)
+    if (chown (buf, d_uid, d_gid) != 0) {
+        fprintf (stderr, "fmadm:  error setting ownership on %s\n", buf);
+        exit (1);
+    }
+#endif        
+    
+    fprintf (stderr, "fmadm:  copying routines from '%s' to '%s'...\n", src_dir, dest_dir);
 
-                if (cp (outfile, infile) != 0) {
-                    fprintf (stderr, "fmadm:  failure copying %s to %s\n", infile, outfile);
-                }
+    if ((dir = opendir (src_dir)) == NULL) {
+        fprintf (stderr, "\nfmadm:  could not open source directory %s\n", src_dir);
+        exit (1);
+    }
+    
+    while ((ent = readdir (dir)) != NULL) {
+        char infile[4096];
+        char outfile[4096];
+        
+        if ((strcmp (ent->d_name, ".") != 0) && (strcmp (ent->d_name, "..") != 0)) {
+            
+            fprintf (stderr, "\t%s\n", ent->d_name);
+            
+            snprintf (infile, 4095, "%s/%s", src_dir, ent->d_name);
+            snprintf (outfile, 4095, "%s/%s", dest_dir, ent->d_name);
 
+#if !defined(__OS2__)            
+            if (cp (outfile, infile) != 0) {
+                fprintf (stderr, "fmadm:  failure copying %s to %s\n", infile, outfile);
             }
+            if (chown (outfile, d_uid, d_gid) != 0) {
+                fprintf (stderr, "fmadm:  error setting ownership on %s\n", outfile);
+                exit (1);
+            }
+#else
+            if (DosCopy (infile, outfile, 1) != 0) {
+                fprintf (stderr, "fmadm:  failure copying %s to %s\n", infile, outfile);
+            }
+#endif
             
         }
-            
         
     }
-    
    
     fp = fopen (config_file, "a+");
     
-
     printf ("Creating %s... ", config_file); 
     
     snprintf (buf, 4095, "[SYSTEM]");
@@ -1274,6 +1433,14 @@ void fm_configure (void)
     
     fclose (fp);
 
+#if !defined(__OS2__)
+    if (chown (config_file, d_uid, d_gid) != 0) {
+        printf ("[FAIL]\n\n");
+        fprintf (stderr, "fmadm:  error setting ownership on %s\n", config_file);
+        exit (1);
+    }
+#endif    
+    
     printf ("[OK]\n\n");
 
 /*