1: /*
2: * $Id: iniconf.c,v 1.11 2025/04/15 18:19:40 snw Exp $
3: * Function implementations for reading
4: * FreeM configuration files
5: *
6: *
7: * Author: Serena Willis <snw@coherent-logic.com>
8: * Copyright (C) 1998 MUG Deutschland
9: * Copyright (C) 2020, 2025 Coherent Logic Development LLC
10: *
11: *
12: * This file is part of FreeM.
13: *
14: * FreeM is free software: you can redistribute it and/or modify
15: * it under the terms of the GNU Affero Public License as published by
16: * the Free Software Foundation, either version 3 of the License, or
17: * (at your option) any later version.
18: *
19: * FreeM is distributed in the hope that it will be useful,
20: * but WITHOUT ANY WARRANTY; without even the implied warranty of
21: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22: * GNU Affero Public License for more details.
23: *
24: * You should have received a copy of the GNU Affero Public License
25: * along with FreeM. If not, see <https://www.gnu.org/licenses/>.
26: *
27: * $Log: iniconf.c,v $
28: * Revision 1.11 2025/04/15 18:19:40 snw
29: * Further attempts to fix FreeBSD
30: *
31: * Revision 1.10 2025/04/15 16:49:36 snw
32: * Make use of logprintf throughout codebase
33: *
34: * Revision 1.9 2025/04/13 04:22:43 snw
35: * Fix snprintf calls
36: *
37: * Revision 1.8 2025/04/10 15:31:25 snw
38: * Attempt to fix DosCopy calls for OS/2
39: *
40: * Revision 1.7 2025/04/10 15:27:39 snw
41: * Detect Devuan distribution and fix OS/2 problem with iniconf.c
42: *
43: * Revision 1.6 2025/04/10 01:24:38 snw
44: * Remove C++ style comments
45: *
46: * Revision 1.5 2025/04/09 19:52:02 snw
47: * Eliminate as many warnings as possible while building with -Wall
48: *
49: * Revision 1.4 2025/04/02 19:59:38 snw
50: * Automatically modify env.conf from fmadm reconfigure
51: *
52: * Revision 1.3 2025/03/09 19:14:25 snw
53: * First phase of REUSE compliance and header reformat
54: *
55: *
56: * SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
57: * SPDX-License-Identifier: AGPL-3.0-or-later
58: **/
59:
60: #define FALSE 0
61: #define TRUE 1
62: #include <stddef.h>
63: #include "iniconf.h"
64: #include "libfill.h"
65: #include <stdio.h>
66: #include <unistd.h>
67: #include <sys/types.h>
68: #include <pwd.h>
69: #include <string.h>
70: #include <limits.h>
71: #include <fs.h>
72: #include "mpsdef.h"
73:
74: #if defined(__OS2__)
75: # include <os2.h>
76: #endif
77:
78: extern char config_file[4096];
79:
80: #if !defined(PATH_MAX) && defined(_SCO_DS)
81: # define PATH_MAX 4096
82: #endif
83:
84: #if !defined(PATH_MAX) && defined(__gnu_hurd__)
85: # define PATH_MAX 1024
86: #endif
87:
88: #if !defined(PATH_MAX) && defined(_AIX)
89: # define PATH_MAX 1024
90: #endif
91:
92: #if defined(__NetBSD__) || defined(__FreeBSD__)
93: # include <sys/syslimits.h>
94: #endif
95:
96: #if !defined(PATH_MAX)
97: # define PATH_MAX 4096
98: #endif
99:
100:
101: int get_conf(char *section, char *key, char *value)
102: {
103: char *etcfile;
104: char *dotfile;
105: char *homedir;
106:
107: int exists_in_etc = FALSE;
108: int exists_in_dotfile = FALSE;
109: int dotexists;
110: int etcexists;
111:
112: char *etc_value;
113: char *dot_value;
114:
115: etc_value = (char *) malloc(CONF_BUFSIZE);
116: NULLPTRCHK(etc_value,"get_conf");
117: dot_value = (char *) malloc(CONF_BUFSIZE);
118: NULLPTRCHK(dot_value,"get_conf");
119:
120: etcfile = config_file;
121:
122:
123: #if !defined(__AMIGA) && !defined(_SCO_DS) && !defined(_AIX)
124: uid_t uid = geteuid();
125: struct passwd *pw = getpwuid(uid);
126:
127: if (pw == NULL) {
128: free (etc_value);
129: free (dot_value);
130:
131: return (FALSE);
132: }
133:
134: homedir = (char *) calloc(strlen(pw->pw_dir) + 1, sizeof(char));
135: NULLPTRCHK(homedir,"get_conf");
136:
137: (void) strcpy(homedir, pw->pw_dir);
138:
139: dotfile = calloc(PATH_MAX, sizeof(char));
140: NULLPTRCHK(dotfile,"get_conf");
141:
142: (void) strcat(dotfile, homedir);
143: (void) strcat(dotfile, "/.freemrc");
144:
145: etcexists = file_exists(etcfile);
146: dotexists = file_exists(dotfile);
147: #else
148:
149: #if defined(__AMIGA)
150: strcpy (etcfile, "./freem.conf");
151: etcexists = TRUE;
152: dotexists = FALSE;
153: #else
154: strcpy (etcfile, SYSCONFDIR"/freem.conf");
155:
156: etcexists = TRUE;
157: dotexists = FALSE;
158: #endif
159:
160: #endif
161:
162: if (etcexists == TRUE) {
163: exists_in_etc = read_profile_string(etcfile, section, key, etc_value);
164: }
165: else {
166: exists_in_etc = FALSE;
167: }
168:
169: if (dotexists == TRUE) {
170: exists_in_dotfile = read_profile_string(dotfile, section, key, dot_value);
171: }
172: else {
173: exists_in_dotfile = FALSE;
174: }
175:
176: if (exists_in_dotfile) {
177: strcpy (value, dot_value);
178:
179: free (etc_value);
180: free (dot_value);
181: #if !defined(__AMIGA) && !defined(_SCO_DS) && !defined(_AIX)
182: free (homedir);
183: free (dotfile);
184: #endif
185:
186: return (TRUE);
187: }
188:
189: if (exists_in_etc) {
190: strcpy(value, etc_value);
191:
192: free (etc_value);
193: free (dot_value);
194: #if !defined(__AMIGA) && !defined(_SCO_DS) && !defined(_AIX)
195: free (homedir);
196: free (dotfile);
197: #endif
198:
199: return (TRUE);
200: }
201:
202: free (etc_value);
203: free (dot_value);
204: #if !defined(__AMIGA) && !defined(_SCO_DS) && !defined(_AIX)
205: free (homedir);
206: free (dotfile);
207: #endif
208:
209: return (FALSE); /* didn't exist anywhere */
210: }
211:
212: int read_profile_string(char *file, char *section, char *key, char *value)
213: {
214:
215: register int i;
216:
217: FILE *fp;
218:
219: char *curkey;
220: char *curval;
221: char *fullsec;
222: char *cursec;
223: char *line;
224: int lnum = 0;
225:
226: curkey = (char *) NULL;
227:
228: fullsec = (char *) malloc(CONF_BUFSIZE);
229: NULLPTRCHK(fullsec,"read_profile_string");
230:
231: cursec = (char *) malloc(CONF_BUFSIZE);
232: NULLPTRCHK(cursec,"read_profile_string");
233:
234: line = (char *) malloc(CONF_BUFSIZE);
235: NULLPTRCHK(line,"read_profile_string");
236:
237:
238:
239:
240: snprintf(fullsec, CONF_BUFSIZE - 1, "[%s]%c", section, '\0');
241:
242: strcpy(cursec, "[]");
243: if ((fp = fopen(file, "r")) == NULL) {
244: return FALSE;
245: }
246:
247: while(fgets(line, CONF_BUFSIZE - 1, fp) != NULL) {
248: ++lnum;
249:
250: if(line[0] == '[') {
251: strcpy(cursec, line);
252:
253: for(i = 0; i < CONF_BUFSIZE; i++) {
254: if(cursec[i] == ']') {
255: cursec[i + 1] = '\0';
256: break;
257: }
258: }
259: }
260: else {
261: if ((line[0] != '[') && (strchr(line, '=') != NULL)) {
262: curkey = strtok(line, "=");
263: curval = strtok(NULL, "=");
264: curval = strtok(curval, "\n");
265:
266:
267: if((strcmp(curkey, key) == 0) && (strcmp(cursec, fullsec) == 0)) {
268: strcpy(value, curval);
269: (void) fclose(fp);
270:
271: free (fullsec);
272: free (curkey);
273: free (cursec);
274:
275: return(TRUE);
276: }
277: }
278: }
279:
280: }
281:
282: if (fp != NULL) {
283: (void) fclose(fp);
284: }
285:
286: /* if we've gotten here, the section and/or key was not found */
287: sprintf (value, "\0");
288:
289: free (fullsec);
290: free (curkey);
291: free (cursec);
292:
293: return(FALSE);
294:
295: }
296:
297: int file_exists(char *filename)
298: {
299: FILE *fp;
300:
301: if ((fp = fopen(filename, "r")) != NULL) {
302: (void) fclose(fp);
303:
304: return(TRUE);
305: }
306: else {
307: return(FALSE);
308: }
309: }
310:
311: void parse_section_header(char *input, char *buf, size_t buflen)
312: {
313: strncpy (buf, &(input[1]), buflen);
314: buf[strlen (buf) - 2] = '\0';
315: }
316:
317: int modify_profile_string(char *file, char *section, char *key, char *value)
318: {
319: FILE *input_fp;
320: FILE *output_fp;
321: char output_filename[4096];
322: char input_line[255];
323: char current_section[255];
324: char *savptr;
325: char *current_key;
326: char tmpsi[255];
327: char tmps[255];
328: int changed = FALSE;
329:
330: snprintf (output_filename, sizeof (output_filename) - 1, "%s.tmp", file);
331:
332: if ((input_fp = fopen (file, "r")) == NULL) {
333: return FALSE;
334: }
335:
336: if ((output_fp = fopen (output_filename, "w+")) == NULL) {
337: fclose (input_fp);
338: return FALSE;
339: }
340:
341: while (fgets (input_line, 255, input_fp)) {
342: if (input_line[0] == '[') {
343: /* this is a section; write it unchanged */
344: fputs (input_line, output_fp);
345:
346: /* update the current section */
347: parse_section_header (input_line, current_section, 255);
348: }
349: else {
350: /* is this a key/value pair? */
351: if (strchr (input_line, '=') != NULL) {
352: /* are we in the requested section? */
353: if (strcmp (section, current_section) == 0) {
354: /* yes; we'll parse it */
355: strcpy (tmpsi, input_line);
356: savptr = tmpsi;
357: current_key = strtok_r (tmpsi, "=", &savptr);
358: /* is this key the one we're changing? */
359: if (strcmp (key, current_key) == 0) {
360: /* yes; modify it and write out*/
361: snprintf (tmps, sizeof (tmps) - 1, "%s=%s\n", current_key, value);
362:
363: fputs (tmps, output_fp);
364: changed = TRUE;
365: }
366: else {
367: /* not the requested key; pass it through unmodified */
368: fputs (input_line, output_fp);
369: }
370: }
371: else {
372: /* not the requested section; write it out unchanged */
373: fputs (input_line, output_fp);
374: }
375: }
376: else {
377: /* not a key/value pair; write it out unchanged */
378: fputs (input_line, output_fp);
379: }
380: }
381: }
382:
383: /* close both files */
384: fclose (output_fp);
385: fclose (input_fp);
386:
387: /* delete the original file */
388: unlink (file);
389:
390: /* rename the temporary file */
391: #if !defined(__OS2__)
392: cp (file, output_filename);
393: #else
394: DosCopy (output_filename, file, 1);
395: #endif
396:
397: return changed;
398: }
399:
400:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>