Xft support under OpenMotif 2.3.3 - I've been using this for quite a while on
[nedit.git] / util / utils.c
blob9bc39d1f6ca6a6f4e35bb52623c7b09e00f88478
1 static const char CVSID[] = "$Id: utils.c,v 1.27 2008/01/04 22:11:05 yooden 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 /* return non-NULL value for the current working directory.
67 If system call fails, provide a fallback value */
68 const char* GetCurrentDir(void)
70 static char curdir[MAXPATHLEN];
72 if (!getcwd(curdir, (size_t) MAXPATHLEN)) {
73 perror("nedit: getcwd() fails");
74 strcpy(curdir, ".");
76 return (curdir);
80 /* return a non-NULL value for the user's home directory,
81 without trailing slash.
82 We try the environment var and the system user database. */
83 const char* GetHomeDir(void)
85 const char *ptr;
86 static char homedir[MAXPATHLEN]="";
87 struct passwd *passwdEntry;
88 size_t len;
90 if (*homedir) {
91 return homedir;
93 ptr=getenv("HOME");
94 if (!ptr) {
95 passwdEntry = getpwuid(getuid());
96 if (passwdEntry && *(passwdEntry->pw_dir)) {
97 ptr= passwdEntry->pw_dir;
98 } else {
99 /* This is really serious, so just exit. */
100 perror("nedit: getpwuid() failed ");
101 exit(EXIT_FAILURE);
104 strncpy(homedir, ptr, sizeof(homedir)-1);
105 homedir[sizeof(homedir)-1]='\0';
106 /* Fix trailing slash */
107 len=strlen(homedir);
108 if (len>1 && homedir[len-1]=='/') {
109 homedir[len-1]='\0';
111 return homedir;
115 ** Return a pointer to the username of the current user in a statically
116 ** allocated string.
118 const char
119 *GetUserName(void)
121 #ifdef VMS
122 return cuserid(NULL);
123 #else
124 /* cuserid has apparently been dropped from the ansi C standard, and if
125 strict ansi compliance is turned on (on Sun anyhow, maybe others), calls
126 to cuserid fail to compile. Older versions of nedit try to use the
127 getlogin call first, then if that fails, use getpwuid and getuid. This
128 results in the user-name of the original terminal being used, which is
129 not correct when the user uses the su command. Now, getpwuid only: */
131 const struct passwd *passwdEntry;
132 static char *userName=NULL;
134 if (userName)
135 return userName;
137 passwdEntry = getpwuid(getuid());
138 if (!passwdEntry) {
139 /* This is really serious, but sometimes username service
140 is misconfigured through no fault of the user. Be nice
141 and let the user start nc anyway. */
142 perror("nedit: getpwuid() failed - reverting to $USER");
143 return getenv("USER");
145 else {
146 userName=malloc(strlen(passwdEntry->pw_name)+1);
147 strcpy(userName, passwdEntry->pw_name);
148 return userName;
150 #endif /* VMS */
155 ** Writes the hostname of the current system in string "hostname".
157 ** NOTE: This function used to be called "GetHostName" but that resulted in a
158 ** linking conflict on VMS with the standard gethostname function, because
159 ** VMS links case-insensitively.
161 const char
162 *GetNameOfHost(void)
164 static char hostname[MAXNODENAMELEN+1];
165 static int hostnameFound = False;
167 if (!hostnameFound) {
168 #ifdef VMS
169 /* This should be simple, but uname is not supported in the DEC C RTL and
170 gethostname on VMS depends either on Multinet or UCX. So use uname
171 on Unix, and use LIB$GETSYI on VMS. Note the VMS hostname will
172 be in DECNET format with trailing double colons, e.g. "FNALV1::". */
173 int syi_status;
174 struct dsc$descriptor_s *hostnameDesc;
175 unsigned long int syiItemCode = SYI$_NODENAME; /* get Nodename */
176 unsigned long int unused = 0;
177 unsigned short int hostnameLen = MAXNODENAMELEN+1;
179 hostnameDesc = NulStrWrtDesc(hostname, MAXNODENAMELEN+1);
180 syi_status = lib$getsyi(&syiItemCode, &unused, hostnameDesc, &hostnameLen,
181 0, 0);
182 if (syi_status != SS$_NORMAL) {
183 fprintf(stderr, "nedit: Error return from lib$getsyi: %d", syi_status);
184 strcpy(hostname, "VMS");
185 } else
186 hostname[hostnameLen] = '\0';
187 FreeStrDesc(hostnameDesc);
188 #else
189 struct utsname nameStruct;
190 int rc = uname(&nameStruct);
191 if (rc<0) {
192 /* Shouldn't ever happen, so we better exit() here */
193 perror("nedit: uname() failed ");
194 exit(EXIT_FAILURE);
196 strcpy(hostname, nameStruct.nodename);
197 #endif /* VMS */
198 hostnameFound = True;
200 return hostname;
205 ** Create a path: $HOME/filename
206 ** Return "" if it doesn't fit into the buffer
208 char
209 *PrependHome(const char *filename, char *buf, size_t buflen)
211 const char *homedir;
212 size_t home_len, file_len;
214 homedir=GetHomeDir();
215 home_len=strlen(homedir);
216 file_len=strlen(filename);
217 if ( (home_len+1+file_len)>=buflen ) {
218 buf[0]='\0';
220 else {
221 strcpy(buf, homedir);
222 strcat(buf, "/");
223 strcat(buf, filename);
225 return buf;
228 int Min(int i1, int i2)
230 return i1 <= i2 ? i1 : i2;
234 ** Returns a pointer to the name of an rc file of the requested type.
236 ** Preconditions:
237 ** - MAXPATHLEN is set to the max. allowed path length
238 ** - fullPath points to a buffer of at least MAXPATHLEN
240 ** Returns:
241 ** - NULL if an error occurs while creating a directory
242 ** - Pointer to a static array containing the file name
245 const char* GetRCFileName(int type)
247 static char rcFiles[N_FILE_TYPES][MAXPATHLEN + 1];
248 static int namesDetermined = False;
250 if (!namesDetermined)
252 char* nedit_home;
253 int i;
255 if ((nedit_home = getenv("NEDIT_HOME")) == NULL)
257 /* No NEDIT_HOME */
258 #ifdef VMS
259 /* This is a default VMS setup */
260 for (i = 0; i < N_FILE_TYPES; i++)
262 buildFilePath(rcFiles[i], "SYS$LOGIN", hiddenFileNames[i]);
264 #else /* #ifdef VMS */
265 /* Let's try if ~/.nedit is a regular file or not. */
266 char legacyFile[MAXPATHLEN + 1];
267 buildFilePath(legacyFile, GetHomeDir(), hiddenFileNames[NEDIT_RC]);
268 if (isRegFile(legacyFile))
270 /* This is a legacy setup with rc files in $HOME */
271 for (i = 0; i < N_FILE_TYPES; i++)
273 buildFilePath(rcFiles[i], GetHomeDir(), hiddenFileNames[i]);
275 } else
277 /* ${HOME}/.nedit does not exist as a regular file. */
278 /* FIXME: Devices, sockets and fifos are ignored for now. */
279 char defaultNEditHome[MAXPATHLEN + 1];
280 buildFilePath(defaultNEditHome, GetHomeDir(), DEFAULT_NEDIT_HOME);
281 if (!isDir(defaultNEditHome))
283 /* Create DEFAULT_NEDIT_HOME */
284 if (mkdir(defaultNEditHome, 0777) != 0)
286 perror("nedit: Error while creating rc file directory"
287 " $HOME/" DEFAULT_NEDIT_HOME "\n"
288 " (Make sure all parent directories exist.)");
289 return NULL;
293 /* All set for DEFAULT_NEDIT_HOME, let's copy the names */
294 for (i = 0; i < N_FILE_TYPES; i++)
296 buildFilePath(rcFiles[i], defaultNEditHome, plainFileNames[i]);
299 #endif /* #ifdef VMS */
300 } else
302 /* $NEDIT_HOME is set. */
303 #ifndef VMS
304 /* FIXME: Is this required? Does VMS know stat(), mkdir()? */
305 if (!isDir(nedit_home))
307 /* Create $NEDIT_HOME */
308 if (mkdir(nedit_home, 0777) != 0)
310 perror("nedit: Error while creating rc file directory $NEDIT_HOME\n"
311 "nedit: (Make sure all parent directories exist.)");
312 return NULL;
315 #endif /* #ifndef VMS */
317 /* All set for NEDIT_HOME, let's copy the names */
318 for (i = 0; i < N_FILE_TYPES; i++)
320 buildFilePath(rcFiles[i], nedit_home, plainFileNames[i]);
324 namesDetermined = True;
327 return rcFiles[type];
331 ** Builds a file path from 'dir' and 'file', watching for buffer overruns.
333 ** Preconditions:
334 ** - MAXPATHLEN is set to the max. allowed path length
335 ** - 'fullPath' points to a buffer of at least MAXPATHLEN
336 ** - 'dir' and 'file' are valid strings
338 ** Postcondition:
339 ** - 'fullpath' will contain 'dir/file'
340 ** - Exits when the result would be greater than MAXPATHLEN
342 static void buildFilePath(char* fullPath, const char* dir, const char* file)
344 if ((MAXPATHLEN) < strlen(dir) + strlen(file) + 2)
346 /* We have no way to build the path. */
347 fprintf(stderr, "nedit: rc file path too long for %s.\n", file);
348 exit(EXIT_FAILURE);
351 /* The length is already checked */
352 strcpy(fullPath, dir);
353 #ifdef VMS
354 strcat(fullPath, ":");
355 #else /* #ifdef VMS */
356 strcat(fullPath, "/");
357 #endif /* #ifdef VMS */
358 strcat(fullPath, file);
362 ** Returns true if 'file' is a directory, false otherwise.
363 ** Links are followed.
365 ** Preconditions:
366 ** - None
368 ** Returns:
369 ** - True for directories, false otherwise
371 static Boolean isDir(const char* file)
373 struct stat attribute;
375 return ((stat(file, &attribute) == 0) && S_ISDIR(attribute.st_mode));
379 ** Returns true if 'file' is a regular file, false otherwise.
380 ** Links are followed.
382 ** Preconditions:
383 ** - None
385 ** Returns:
386 ** - True for regular files, false otherwise
388 static Boolean isRegFile(const char* file)
390 struct stat attribute;
392 return ((stat(file, &attribute) == 0) && S_ISREG(attribute.st_mode));
396 ** Part of the simple stack. Accepts a stack and the pointer you want to
397 ** store. NULL is not allowed, as it is used in Pop() to signal an empty
398 ** stack.
400 void Push(Stack* stack, const void* value)
402 stackObject* pushee;
404 /* Throw away invalid parameters. */
405 if (NULL == value) {
406 fprintf(stderr, "nedit: Internal error: NULL was pushed.\n");
407 return;
409 if (NULL == stack) {
410 fprintf(stderr, "nedit: Internal error: push() called with NULL stack.\n");
411 return;
414 /* Allocate memory for new value. */
415 pushee = (stackObject*) XtMalloc(sizeof(stackObject));
417 /* Put pushee on top of stack. */
418 pushee->value = (void*) value;
419 pushee->next = stack->top;
420 stack->top = pushee;
421 (stack->size)++;
423 return;
427 ** Part of the simple stack, returns the topmost item from the stack or
428 ** NULL if the stack is empty. It also returns NULL if the stack itself
429 ** is NULL.
431 ** Precondition: The stack's top element is either NULL or a properly
432 ** initialised stackObject.
434 void* Pop(Stack* stack)
436 stackObject* popee;
437 void* value;
439 /* Throw away invalid parameter. */
440 if (NULL == stack) {
441 fprintf(stderr, "nedit: Internal error: pop() called with NULL stack.\n");
442 return NULL;
445 /* Return NULL if Stack is empty. */
446 if (NULL == stack->top) {
447 return NULL;
450 /* Remove top entry in the stack. */
451 popee = stack->top;
452 stack->top = popee->next;
453 (stack->size)--;
455 value = popee->value;
456 XtFree((char*) popee);
458 return value;
462 ** We currently don't need this function: In the only situation where we use
463 ** the stack, we empty it completely. This might come in handy if the stack
464 ** is ever used anywhere else.
466 ** Beware: Utterly untested.
467 */ /*
468 void FreeStack(Stack* stack)
470 void* dummy;
472 while (NULL != (dummy = pop(progStack))) {}
474 XtFree((char*) stack);