1 static const char CVSID
[] = "$Id: utils.c,v 1.27 2008/01/04 22:11:05 yooden Exp $";
2 /*******************************************************************************
4 * utils.c -- miscellaneous non-GUI routines *
6 * Copyright (C) 2002 Mark Edel *
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. *
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.* *
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 *
23 *******************************************************************************/
26 #include "../config.h"
36 #include <lib$routines.h>
39 #include "../util/VMSparam.h"
40 #include "../util/VMSutils.h"
42 #include <sys/types.h>
46 /* just to get 'Boolean' types defined: */
47 #include <X11/Intrinsic.h>
53 #define DEFAULT_NEDIT_HOME ".nedit"
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"};
58 static char* hiddenFileNames
[N_FILE_TYPES
] = {".nedit", ".neditmacro", ".neditdb"};
59 static char* plainFileNames
[N_FILE_TYPES
] = {"nedit.rc", "autoload.nm", "nedit.history"};
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");
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)
86 static char homedir
[MAXPATHLEN
]="";
87 struct passwd
*passwdEntry
;
95 passwdEntry
= getpwuid(getuid());
96 if (passwdEntry
&& *(passwdEntry
->pw_dir
)) {
97 ptr
= passwdEntry
->pw_dir
;
99 /* This is really serious, so just exit. */
100 perror("nedit: getpwuid() failed ");
104 strncpy(homedir
, ptr
, sizeof(homedir
)-1);
105 homedir
[sizeof(homedir
)-1]='\0';
106 /* Fix trailing slash */
108 if (len
>1 && homedir
[len
-1]=='/') {
115 ** Return a pointer to the username of the current user in a statically
122 return cuserid(NULL
);
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
;
137 passwdEntry
= getpwuid(getuid());
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");
146 userName
=malloc(strlen(passwdEntry
->pw_name
)+1);
147 strcpy(userName
, passwdEntry
->pw_name
);
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.
164 static char hostname
[MAXNODENAMELEN
+1];
165 static int hostnameFound
= False
;
167 if (!hostnameFound
) {
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::". */
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
,
182 if (syi_status
!= SS$_NORMAL
) {
183 fprintf(stderr
, "nedit: Error return from lib$getsyi: %d", syi_status
);
184 strcpy(hostname
, "VMS");
186 hostname
[hostnameLen
] = '\0';
187 FreeStrDesc(hostnameDesc
);
189 struct utsname nameStruct
;
190 int rc
= uname(&nameStruct
);
192 /* Shouldn't ever happen, so we better exit() here */
193 perror("nedit: uname() failed ");
196 strcpy(hostname
, nameStruct
.nodename
);
198 hostnameFound
= True
;
205 ** Create a path: $HOME/filename
206 ** Return "" if it doesn't fit into the buffer
209 *PrependHome(const char *filename
, char *buf
, size_t buflen
)
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
) {
221 strcpy(buf
, homedir
);
223 strcat(buf
, filename
);
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.
237 ** - MAXPATHLEN is set to the max. allowed path length
238 ** - fullPath points to a buffer of at least MAXPATHLEN
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
)
255 if ((nedit_home
= getenv("NEDIT_HOME")) == NULL
)
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
]);
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.)");
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 */
302 /* $NEDIT_HOME is set. */
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.)");
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.
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
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
);
351 /* The length is already checked */
352 strcpy(fullPath
, dir
);
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.
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.
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
400 void Push(Stack
* stack
, const void* value
)
404 /* Throw away invalid parameters. */
406 fprintf(stderr
, "nedit: Internal error: NULL was pushed.\n");
410 fprintf(stderr
, "nedit: Internal error: push() called with NULL stack.\n");
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
;
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
431 ** Precondition: The stack's top element is either NULL or a properly
432 ** initialised stackObject.
434 void* Pop(Stack
* stack
)
439 /* Throw away invalid parameter. */
441 fprintf(stderr
, "nedit: Internal error: pop() called with NULL stack.\n");
445 /* Return NULL if Stack is empty. */
446 if (NULL
== stack
->top
) {
450 /* Remove top entry in the stack. */
452 stack
->top
= popee
->next
;
455 value
= popee
->value
;
456 XtFree((char*) popee
);
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.
468 void FreeStack(Stack* stack)
472 while (NULL != (dummy = pop(progStack))) {}
474 XtFree((char*) stack);