Patch from ICS to do proper subclassing - this will make it more immune to
[nedit.git] / util / utils.c
blobd8444fe7d0379475be8cd37cc585d8a8d9ac30b5
1 static const char CVSID[] = "$Id: utils.c,v 1.23 2004/10/18 19:27:26 arnef Exp $";
2 /*******************************************************************************
3 * *
4 * utils.c -- miscellaneous non-GUI routines *
5 * *
6 * Copyright (C) 2002 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. In addition, you may distribute versions of this program linked to *
12 * Motif or Open Motif. See README for details. *
13 * *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
17 * for more details.* *
18 * *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * *
23 *******************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 #include "../config.h"
27 #endif
29 #include "utils.h"
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <unistd.h>
35 #ifdef VMS
36 #include <lib$routines.h>
37 #include ssdef
38 #include syidef
39 #include "../util/VMSparam.h"
40 #include "../util/VMSutils.h"
41 #endif
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <pwd.h>
46 /* just to get 'Boolean' types defined: */
47 #include <X11/Intrinsic.h>
49 #ifdef HAVE_DEBUG_H
50 #include "../debug.h"
51 #endif
53 #define DEFAULT_NEDIT_HOME ".nedit"
54 #ifdef VMS
55 static char* hiddenFileNames[N_FILE_TYPES] = {".nedit", ".neditmacro", ".neditdb;1"};
56 static char* plainFileNames[N_FILE_TYPES] = {"nedit.rc", "autoload.nm", "nedit.history;1"};
57 #else
58 static char* hiddenFileNames[N_FILE_TYPES] = {".nedit", ".neditmacro", ".neditdb"};
59 static char* plainFileNames[N_FILE_TYPES] = {"nedit.rc", "autoload.nm", "nedit.history"};
60 #endif
62 static void buildFilePath(char* fullPath, const char* dir, const char* file);
63 static Boolean isDir(const char* file);
64 static Boolean isRegFile(const char* file);
66 extern const char
67 *GetCurrentDir(void)
68 /* return non-NULL value for the current working directory.
69 If system call fails, provide a fallback value */
71 static char curdir[MAXPATHLEN];
73 if (!getcwd(curdir, MAXPATHLEN)) {
74 perror("nedit: getcwd() fails");
75 strcpy(curdir, ".");
77 return (curdir);
81 extern const char
82 *GetHomeDir(void)
83 /* return a non-NULL value for the user's home directory,
84 without trailing slash.
85 We try the environment var and the system user database. */
87 const char *ptr;
88 static char homedir[MAXPATHLEN]="";
89 struct passwd *passwdEntry;
90 int len;
92 if (*homedir) {
93 return homedir;
95 ptr=getenv("HOME");
96 if (!ptr) {
97 passwdEntry = getpwuid(getuid());
98 if (passwdEntry && *(passwdEntry->pw_dir)) {
99 ptr= passwdEntry->pw_dir;
100 } else {
101 /* This is really serious, so just exit. */
102 perror("nedit: getpwuid() failed ");
103 exit(EXIT_FAILURE);
106 strncpy(homedir, ptr, sizeof(homedir)-1);
107 homedir[sizeof(homedir)-1]='\0';
108 /* Fix trailing slash */
109 len=strlen(homedir);
110 if (len>1 && homedir[len-1]=='/') {
111 homedir[len-1]='\0';
113 return homedir;
117 ** Return a pointer to the username of the current user in a statically
118 ** allocated string.
120 const char
121 *GetUserName(void)
123 #ifdef VMS
124 return cuserid(NULL);
125 #else
126 /* cuserid has apparently been dropped from the ansi C standard, and if
127 strict ansi compliance is turned on (on Sun anyhow, maybe others), calls
128 to cuserid fail to compile. Older versions of nedit try to use the
129 getlogin call first, then if that fails, use getpwuid and getuid. This
130 results in the user-name of the original terminal being used, which is
131 not correct when the user uses the su command. Now, getpwuid only: */
133 const struct passwd *passwdEntry;
134 static char *userName=NULL;
136 if (userName)
137 return userName;
139 passwdEntry = getpwuid(getuid());
140 if (!passwdEntry) {
141 /* This is really serious, but sometimes username service
142 is misconfigured through no fault of the user. Be nice
143 and let the user start nc anyway. */
144 perror("nedit: getpwuid() failed - reverting to $USER");
145 return getenv("USER");
147 else {
148 userName=malloc(strlen(passwdEntry->pw_name)+1);
149 strcpy(userName, passwdEntry->pw_name);
150 return userName;
152 #endif /* VMS */
157 ** Writes the hostname of the current system in string "hostname".
159 ** NOTE: This function used to be called "GetHostName" but that resulted in a
160 ** linking conflict on VMS with the standard gethostname function, because
161 ** VMS links case-insensitively.
163 const char
164 *GetNameOfHost(void)
166 static char hostname[MAXNODENAMELEN+1];
167 static int hostnameFound = False;
169 if (!hostnameFound) {
170 #ifdef VMS
171 /* This should be simple, but uname is not supported in the DEC C RTL and
172 gethostname on VMS depends either on Multinet or UCX. So use uname
173 on Unix, and use LIB$GETSYI on VMS. Note the VMS hostname will
174 be in DECNET format with trailing double colons, e.g. "FNALV1::". */
175 int syi_status;
176 struct dsc$descriptor_s *hostnameDesc;
177 unsigned long int syiItemCode = SYI$_NODENAME; /* get Nodename */
178 unsigned long int unused = 0;
179 unsigned short int hostnameLen = MAXNODENAMELEN+1;
181 hostnameDesc = NulStrWrtDesc(hostname, MAXNODENAMELEN+1);
182 syi_status = lib$getsyi(&syiItemCode, &unused, hostnameDesc, &hostnameLen,
183 0, 0);
184 if (syi_status != SS$_NORMAL) {
185 fprintf(stderr, "nedit: Error return from lib$getsyi: %d", syi_status);
186 strcpy(hostname, "VMS");
187 } else
188 hostname[hostnameLen] = '\0';
189 FreeStrDesc(hostnameDesc);
190 #else
191 struct utsname nameStruct;
192 int rc = uname(&nameStruct);
193 if (rc<0) {
194 /* Shouldn't ever happen, so we better exit() here */
195 perror("nedit: uname() failed ");
196 exit(EXIT_FAILURE);
198 strcpy(hostname, nameStruct.nodename);
199 #endif /* VMS */
200 hostnameFound = True;
202 return hostname;
207 ** Create a path: $HOME/filename
208 ** Return "" if it doesn't fit into the buffer
210 char
211 *PrependHome(const char *filename, char *buf, int buflen)
213 const char *homedir;
214 int home_len, file_len;
216 homedir=GetHomeDir();
217 home_len=strlen(homedir);
218 file_len=strlen(filename);
219 if ( (home_len+1+file_len)>=buflen ) {
220 buf[0]='\0';
222 else {
223 strcpy(buf, homedir);
224 strcat(buf, "/");
225 strcat(buf, filename);
227 return buf;
230 extern int Max(int i1, int i2)
232 return i1 >= i2 ? i1 : i2;
235 extern int Min(int i1, int i2)
237 return i1 <= i2 ? i1 : i2;
240 extern int Min3(int i1, int i2, int i3)
242 if (i1 <= i2 && i1 <= i3)
243 return i1;
244 return i2 <= i3 ? i2 : i3;
247 extern int Max3(int i1, int i2, int i3)
249 if (i1 >= i2 && i1 >= i3)
250 return i1;
251 return i2 >= i3 ? i2 : i3;
255 ** Returns a pointer to the name of an rc file of the requested type.
257 ** Preconditions:
258 ** - MAXPATHLEN is set to the max. allowed path length
259 ** - fullPath points to a buffer of at least MAXPATHLEN
261 ** Returns:
262 ** - NULL if an error occurs while creating a directory
263 ** - Pointer to a static array containing the file name
266 const char* GetRCFileName(int type)
268 static char rcFiles[N_FILE_TYPES][MAXPATHLEN + 1];
269 static int namesDetermined = False;
271 if (!namesDetermined)
273 char* nedit_home;
274 int i;
276 if ((nedit_home = getenv("NEDIT_HOME")) == NULL)
278 /* No NEDIT_HOME */
279 #ifdef VMS
280 /* This is a default VMS setup */
281 for (i = 0; i < N_FILE_TYPES; i++)
283 buildFilePath(rcFiles[i], "SYS$LOGIN", hiddenFileNames[i]);
285 #else /* #ifdef VMS */
286 /* Let's try if ~/.nedit is a regular file or not. */
287 char legacyFile[MAXPATHLEN + 1];
288 buildFilePath(legacyFile, GetHomeDir(), hiddenFileNames[NEDIT_RC]);
289 if (isRegFile(legacyFile))
291 /* This is a legacy setup with rc files in $HOME */
292 for (i = 0; i < N_FILE_TYPES; i++)
294 buildFilePath(rcFiles[i], GetHomeDir(), hiddenFileNames[i]);
296 } else
298 /* ${HOME}/.nedit does not exist as a regular file. */
299 /* FIXME: Devices, sockets and fifos are ignored for now. */
300 char defaultNEditHome[MAXPATHLEN + 1];
301 buildFilePath(defaultNEditHome, GetHomeDir(), DEFAULT_NEDIT_HOME);
302 if (!isDir(defaultNEditHome))
304 /* Create DEFAULT_NEDIT_HOME */
305 if (mkdir(defaultNEditHome, 0777) != 0)
307 perror("nedit: Error while creating rc file directory"
308 " $HOME/" DEFAULT_NEDIT_HOME "\n"
309 " (Make sure all parent directories exist.)");
310 return NULL;
314 /* All set for DEFAULT_NEDIT_HOME, let's copy the names */
315 for (i = 0; i < N_FILE_TYPES; i++)
317 buildFilePath(rcFiles[i], defaultNEditHome, plainFileNames[i]);
320 #endif /* #ifdef VMS */
321 } else
323 /* $NEDIT_HOME is set. */
324 #ifndef VMS
325 /* FIXME: Is this required? Does VMS know stat(), mkdir()? */
326 if (!isDir(nedit_home))
328 /* Create $NEDIT_HOME */
329 if (mkdir(nedit_home, 0777) != 0)
331 perror("nedit: Error while creating rc file directory $NEDIT_HOME\n"
332 "nedit: (Make sure all parent directories exist.)");
333 return NULL;
336 #endif /* #ifndef VMS */
338 /* All set for NEDIT_HOME, let's copy the names */
339 for (i = 0; i < N_FILE_TYPES; i++)
341 buildFilePath(rcFiles[i], nedit_home, plainFileNames[i]);
345 namesDetermined = True;
348 return rcFiles[type];
352 ** Builds a file path from 'dir' and 'file', watching for buffer overruns.
354 ** Preconditions:
355 ** - MAXPATHLEN is set to the max. allowed path length
356 ** - 'fullPath' points to a buffer of at least MAXPATHLEN
357 ** - 'dir' and 'file' are valid strings
359 ** Postcondition:
360 ** - 'fullpath' will contain 'dir/file'
361 ** - Exits when the result would be greater than MAXPATHLEN
363 static void buildFilePath(char* fullPath, const char* dir, const char* file)
365 if ((MAXPATHLEN) < strlen(dir) + strlen(file) + 2)
367 /* We have no way to build the path. */
368 fprintf(stderr, "nedit: rc file path too long for %s.\n", file);
369 exit(EXIT_FAILURE);
372 /* The length is already checked */
373 strcpy(fullPath, dir);
374 #ifdef VMS
375 strcat(fullPath, ":");
376 #else /* #ifdef VMS */
377 strcat(fullPath, "/");
378 #endif /* #ifdef VMS */
379 strcat(fullPath, file);
383 ** Returns true if 'file' is a directory, false otherwise.
384 ** Links are followed.
386 ** Preconditions:
387 ** - None
389 ** Returns:
390 ** - True for directories, false otherwise
392 static Boolean isDir(const char* file)
394 struct stat attribute;
396 return ((stat(file, &attribute) == 0) && S_ISDIR(attribute.st_mode));
400 ** Returns true if 'file' is a regular file, false otherwise.
401 ** Links are followed.
403 ** Preconditions:
404 ** - None
406 ** Returns:
407 ** - True for regular files, false otherwise
409 static Boolean isRegFile(const char* file)
411 struct stat attribute;
413 return ((stat(file, &attribute) == 0) && S_ISREG(attribute.st_mode));