1: /*
2: * $Id: fma_routines.c,v 1.5 2025/04/13 04:22:43 snw Exp $
3: * fmadm - routines
4: *
5: *
6: * Author: Serena Willis <snw@coherent-logic.com>
7: * Copyright (C) 1998 MUG Deutschland
8: * Copyright (C) 2020, 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_routines.c,v $
27: * Revision 1.5 2025/04/13 04:22:43 snw
28: * Fix snprintf calls
29: *
30: * Revision 1.4 2025/03/22 18:43:54 snw
31: * Make STRLEN 255 chars and add BIGSTR macro for larger buffers
32: *
33: * Revision 1.3 2025/03/09 19:14:25 snw
34: * First phase of REUSE compliance and header reformat
35: *
36: *
37: * SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
38: * SPDX-License-Identifier: AGPL-3.0-or-later
39: **/
40:
41: #include <stdio.h>
42: #include <stdlib.h>
43: #include <string.h>
44: #include <dirent.h>
45: #include <stdlib.h>
46: #include <time.h>
47: #include <unistd.h>
48: #include <sys/types.h>
49: #include <sys/stat.h>
50: #include <ctype.h>
51: #include <errno.h>
52:
53: #include "fmadm.h"
54: #include "iniconf.h"
55:
56: int fma_do_export (FILE *out, char *rtn_name);
57: char *fma_trim_string(char *str);
58:
59: int fma_routines_list (int optc, char **opts)
60: {
61:
62: DIR *dir;
63: struct dirent *ent;
64:
65: char filename[PATHLEN];
66: char *rtnname;
67: char *rtnext;
68:
69: int ct = 0;
70:
71: printf ("\nFreeM Routine Listing\n");
72: printf ("---------------------\n\n");
73:
74: printf ("Namespace: %s\n", fma_namespace);
75: printf ("Routine Path: %s\n\n", fma_routine_path);
76:
77: if ((dir = opendir (fma_routine_path)) == NULL) {
78: fprintf (stderr, "fmadm: could not open routine directory %s\n", fma_routine_path);
79: return 1;
80: }
81:
82: while ((ent = readdir (dir)) != NULL) {
83:
84: strncpy (filename, ent->d_name, PATHLEN - 1);
85:
86: rtnname = strtok (filename, ".");
87: rtnext = strtok (NULL, ".");
88:
89: if (rtnext != NULL && strncmp (rtnext, "m", PATHLEN - 1) == 0) {
90: printf (" %s\n", rtnname);
91: ct++;
92: }
93:
94: }
95:
96: printf ("\n\n - %d routines found\n\n", ct);
97: closedir (dir);
98:
99: return 0;
100:
101: }
102:
103: int fma_routines_edit (int optc, char **opts)
104: {
105: FILE *fp;
106: char rpath[PATHLEN];
107: char ecmd[STRLEN];
108: char *editor;
109:
110: if (optc < fma_min_args) {
111: fprintf (stderr, "fmadm: must supply routine name\n");
112: return 1;
113: }
114:
115:
116: if ((editor = getenv("EDITOR")) == NULL) {
117:
118: if ((editor = (char *) malloc (3 * sizeof (char))) == NULL) {
119: fprintf (stderr, "fmadm: could not acquire memory\n");
120: return 1;
121: }
122:
123: strncpy (editor, "vi", 3);
124:
125: }
126:
127: snprintf (rpath, PATHLEN - 1, "%s/%s.m", fma_routine_path, opts[fma_base_opt]);
128:
129: if (file_exists (rpath) == FALSE) {
130: if ((fp = fopen (rpath, "w")) == NULL) {
131: fprintf (stderr, "fmadm: error %d creating routine %s (%s)\n", errno, opts[1], strerror (errno));
132:
133: return 1;
134: }
135:
136: fprintf (fp, "%s ; Created by FreeM Administrator\n QUIT\n", opts[1]);
137: fclose (fp);
138:
139: }
140:
141: snprintf (ecmd, STRLEN - 1, "%s %s", editor, rpath);
142: system (ecmd);
143:
144: return 0;
145:
146: }
147:
148: int fma_routines_examine (int optc, char **opts)
149: {
150:
151: FILE *fp;
152: char c;
153: char rpath[PATHLEN];
154: /* char *editor; */
155:
156: if (optc < fma_min_args) {
157: fprintf (stderr, "fmadm: must supply routine name\n");
158: return 1;
159: }
160:
161: snprintf (rpath, PATHLEN - 1, "%s/%s.m", fma_routine_path, opts[fma_base_opt]);
162:
163: if (file_exists (rpath) == FALSE) {
164: fprintf (stderr, "fmadm: routine %s does not exist in namespace %s\n", opts[1], fma_namespace);
165: return 1;
166: }
167:
168: if ((fp = fopen (rpath, "r")) == NULL) {
169: fprintf (stderr, "fmadm: could not open routine %s\n", opts[1]);
170: return 1;
171: }
172:
173: while ((c = fgetc (fp)) != EOF) putchar (c);
174:
175: fclose (fp);
176:
177: putchar ('\n');
178:
179: return 0;
180:
181: }
182:
183: int fma_routines_backup (int optc, char **opts)
184: {
185: time_t t = time (NULL);
186: struct tm *buf;
187: char rcmd[STRLEN];
188: char backup_filename[PATHLEN];
189: char dtstamp[STRLEN];
190: char bup_path[PATHLEN];
191:
192: buf = gmtime (&t);
193:
194: if (optc > 1) {
195: strncpy (bup_path, opts[fma_base_opt], PATHLEN - 1);
196: }
197: else {
198: strncpy (bup_path, "/tmp", PATHLEN - 1);
199: }
200:
201: snprintf (dtstamp, STRLEN - 1, "%d%02d%02d%02d%02d%02d", buf->tm_year, buf->tm_mon, buf->tm_mday, buf->tm_hour, buf->tm_min, buf->tm_sec);
202: snprintf (backup_filename, PATHLEN - 1, "%s/freem-routines-backup-%s-%s.tar", bup_path, fma_namespace, dtstamp);
203:
204: printf ("\nFreeM Routine Backup\n");
205: printf ("--------------------\n\n");
206:
207: printf ("Namespace: %s\n", fma_namespace);
208: printf ("Routine Path: %s\n", fma_routine_path);
209: printf ("Backup Location: %s\n", bup_path);
210: printf ("Backup Filename: %s\n\n", backup_filename);
211:
212: snprintf (rcmd, STRLEN - 1, "tar cvf %s %s/*.m", backup_filename, fma_routine_path);
213: system (rcmd);
214:
215: printf ("\n\nBackup completed.\n\n");
216:
217: return 0;
218:
219: }
220:
221: int fma_routines_restore (int optc, char **opts)
222: {
223: return 0;
224: }
225:
226: char *fma_trim_string(char *str)
227: {
228:
229: char *end;
230:
231: while (isspace ((unsigned char) *str)) str++;
232:
233: if (*str == 0) return str;
234:
235: end = str + strlen (str) - 1;
236:
237: while (end > str && isspace ((unsigned char) *end)) end--;
238:
239: end[1] = '\0';
240:
241: return str;
242:
243: }
244:
245: int fma_routines_import (int optc, char **opts)
246: {
247: int usr_loaded = 0;
248: int pct_loaded = 0;
249: int next_routine_flag = FALSE;
250: int rtn_open = FALSE;
251: int rtn_overwrite = TRUE;
252: long ln = 0;
253:
254: char pct_rtn_path[4096];
255: char usr_rtn_path[4096];
256: char namespace[4096];
257: char roufile[4096];
258: char line[STRLEN];
259: char *trimmed_line;
260:
261: char filename[4096];
262: FILE *archive = NULL;
263: FILE *rtn = NULL;
264:
265: char *parsed_line;
266:
267: strncpy (namespace, fma_namespace, 512 - 1);
268: strncpy (filename, opts[fma_base_opt], 4096 - 1);
269:
270: if (optc < fma_min_args) {
271: fprintf (stderr, "usage: fmadm import routine <namespace> <rsav-file>\n");
272: return 1;
273: }
274:
275: if (strncmp (fma_namespace, "SYSTEM", PATHLEN - 1) != 0) {
276:
277: if (get_conf ("SYSTEM", "routines_path", pct_rtn_path) == FALSE) {
278: fprintf (stderr, "fmadm: could not determine percent routine access path from configuration for namespace '%s'.\n", namespace);
279:
280: return 1;
281: }
282:
283: }
284: else {
285: strncpy (pct_rtn_path, fma_routine_path, 4096 - 1);
286: }
287:
288:
289: if (get_conf (namespace, "routines_path", usr_rtn_path) == FALSE) {
290: fprintf (stderr, "fmadm: could not determine user routine access path from configuration for namespace '%s'.\n", namespace);
291:
292: return 1;
293: }
294:
295: if ((archive = fopen (filename, "r")) == NULL) {
296: fprintf (stderr, "fmadm: could not open routine archive '%s'.\n", filename);
297:
298: return 1;
299: }
300:
301: printf ("\nFreeM Routine Import\n");
302: printf ("--------------------\n\n");
303:
304: printf ("Namespace: %s\n", fma_namespace);
305: printf ("Archive: %s\n", filename);
306: printf ("Routine Path: %s\n", usr_rtn_path);
307: printf ("%% Routine Path: %s\n\n", pct_rtn_path);
308:
309: /* read routine archive line-by-line */
310: while (fgets(line, STRLEN - 1, archive) != NULL) {
311:
312: if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = '\0';
313:
314: ln++;
315:
316: if ((ln == 1) || (ln == 2)) {
317: printf("%s\n", line);
318: continue;
319: }
320:
321: if (ln == 3) {
322: next_routine_flag = TRUE;
323: }
324:
325: trimmed_line = strdup (line);
326:
327: if (strcmp (fma_trim_string (trimmed_line), "") == 0) {
328:
329: next_routine_flag = TRUE;
330:
331: if(rtn_open == TRUE) {
332: fclose(rtn);
333:
334: rtn_open = FALSE;
335: }
336:
337: continue;
338: }
339:
340: if(next_routine_flag == TRUE) {
341:
342: next_routine_flag = FALSE;
343:
344: parsed_line = strtok (line, "^");
345:
346: if((!isalpha(line[0])) && (line[0] != '%')) {
347:
348: if(rtn_open == TRUE) {
349: fclose(rtn);
350: rtn_open = FALSE;
351: }
352:
353: continue;
354: }
355:
356: printf(" %s\n", parsed_line);
357:
358:
359: switch(line[0]) {
360:
361: case '%':
362: pct_loaded++;
363:
364: snprintf(roufile, PATH_MAX - 1, "%s/%s.m", pct_rtn_path, parsed_line);
365: break;
366:
367: default:
368: usr_loaded++;
369:
370: snprintf(roufile, PATH_MAX - 1, "%s/%s.m", usr_rtn_path, parsed_line);
371: break;
372: }
373:
374: if((file_exists(roufile) == TRUE) && (rtn_overwrite == FALSE)) {
375: fprintf(stdout, "fmadm: routine file '%s' already exists. Will not overwrite.\n", roufile);
376:
377: fclose (archive);
378:
379: return 1;
380: }
381:
382: if((rtn = fopen(roufile, "w")) == NULL) {
383: fprintf(stdout, "fmadm: could not open routine file '%s'.\n", roufile);
384:
385: return 1;
386: }
387: else {
388: rtn_open = TRUE;
389: }
390:
391: }
392: else {
393: if(rtn_open == TRUE) fprintf(rtn, "%s\n", line);
394: }
395:
396: }
397:
398: printf("\n - loaded %d user routines and %d percent routines (%d total)\n\n", usr_loaded, pct_loaded, usr_loaded + pct_loaded);
399:
400: fclose (archive);
401:
402: return 0;
403: }
404:
405: int fma_routines_export (int optc, char **opts)
406: {
407:
408: FILE *out; /* output file handle */
409: DIR *dir; /* namespace directory */
410:
411: struct dirent *ent;
412: char output_file[PATHLEN];
413: char routine_spec[PATHLEN];
414: char filename[PATHLEN];
415: char *rtnname;
416: char *rtnext;
417: int i;
418:
419: int ct = 0;
420:
421:
422: if (optc < fma_min_args) {
423: fprintf (stderr, "usage: fmadm export routine <namespace> <output-file> [routine1 routine2... routineN]\n");
424: return 1;
425: }
426:
427: /* if routines aren't listed, assume we should export entire namespace */
428: if (optc == fma_min_args) {
429: strncpy (routine_spec, "*", PATHLEN - 1);
430: }
431:
432: strncpy (output_file, opts[1], PATHLEN - 1);
433: strncpy (routine_spec, opts[2], PATHLEN - 1);
434:
435: if (file_exists (output_file) == TRUE) {
436: fprintf (stderr, "fmadm: output file %s already exists\n", output_file);
437: return 1;
438: }
439:
440: if ((out = fopen(output_file, "w")) == NULL) {
441: fprintf (stderr, "fmadm: could not open output file %s for writing\n", output_file);
442: return 1;
443: }
444:
445:
446: printf ("\nFreeM Routine Export\n");
447: printf ("--------------------\n\n");
448:
449: printf ("Namespace: %s\n", fma_namespace);
450: printf ("Routine Path: %s\n", fma_routine_path);
451: printf ("Output File: %s\n", output_file);
452: printf ("Routines Selected: ");
453:
454: if (optc == 2) {
455: printf ("[ENTIRE NAMESPACE]\n\n");
456: }
457: else {
458: for (i = 2; i < optc; i++) {
459: printf ("%s ", opts[i]);
460: }
461:
462: printf ("\n\n");
463: }
464:
465: fprintf (out, "Routines\n");
466:
467: if (optc == 2) {
468:
469: /* export entire namespace */
470:
471: if ((dir = opendir (fma_routine_path)) == NULL) {
472: fprintf (stderr, "fmadm: could not open routine directory %s\n", fma_routine_path);
473: fclose (out);
474: return 1;
475: }
476:
477: while ((ent = readdir (dir)) != NULL) {
478:
479: strncpy (filename, ent->d_name, PATHLEN - 1);
480:
481: rtnname = strtok (filename, ".");
482: rtnext = strtok (NULL, ".");
483:
484: if (rtnext != NULL && strncmp (rtnext, "m", PATHLEN - 1) == 0) {
485: ct += fma_do_export (out, rtnname);
486: }
487:
488: }
489:
490: closedir (dir);
491:
492: }
493: else {
494:
495: /* export only selected routines */
496: for (i = fma_base_opt + 1; i < optc; i++) {
497: ct += fma_do_export (out, opts[i]);
498: }
499:
500: }
501:
502: printf ("\n\n - %d routines exported\n\n", ct);
503:
504: fclose (out);
505:
506: return 0;
507:
508: }
509:
510: int fma_do_export (FILE *out, char *rtn_name)
511: {
512: FILE *in;
513: char rtnfile[PATHLEN];
514: char line[FM_STR_MAX];
515:
516: snprintf (rtnfile, PATHLEN - 1, "%s/%s.m", fma_routine_path, rtn_name);
517:
518: printf (" Exporting %-10s\t", rtn_name);
519:
520: if ((in = fopen (rtnfile, "r")) == NULL) {
521: printf ("[FAIL]\n");
522: return 0;
523: }
524:
525: fprintf (out, "\n%s\n", rtn_name);
526:
527: while (fgets (line, FM_STR_MAX, in) != NULL) {
528: fprintf (out, "%s", line);
529: }
530:
531: printf ("[OK]\n");
532:
533: fprintf (out, "\n");
534:
535: fclose (in);
536:
537: return 1;
538: }
539:
540: int fma_routines_create (int optc, char **opts)
541: {
542: return 0;
543: }
544:
545: int fma_routines_remove (int optc, char **opts)
546: {
547:
548: char rpath[PATHLEN];
549: int i;
550: int ct = 0;
551: int er = 0;
552: int tot = 0;
553:
554: printf ("\nFreeM Routine Removal\n");
555: printf ("---------------------\n\n");
556:
557: printf ("Namespace: %s\n", fma_namespace);
558: printf ("Routine Path: %s\n\n", fma_routine_path);
559:
560: for (i = fma_base_opt; i < optc; i++) {
561: printf ("%-10s\t", opts[i]);
562:
563: snprintf (rpath, PATHLEN - 1, "%s/%s.m", fma_routine_path, opts[i]);
564:
565: if (unlink (rpath) == -1) {
566: printf ("[FAIL]\n");
567: er++;
568: }
569: else {
570: printf ("[OK]\n");
571: ct++;
572: }
573:
574: tot++;
575:
576: }
577:
578: printf ("\nRemoved %d routines [%d errors/%d attempted]\n\n", ct, er, tot);
579:
580: return 0;
581:
582: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>