UNIXworkcode

1 /******************************************************************************* 2 * * 3 * utils.c -- miscellaneous non-GUI routines * 4 * * 5 * Copyright (C) 2002 Mark Edel * 6 * * 7 * This is free software; you can redistribute it and/or modify it under the * 8 * terms of the GNU General Public License as published by the Free Software * 9 * Foundation; either version 2 of the License, or (at your option) any later * 10 * version. In addition, you may distribute versions of this program linked to * 11 * Motif or Open Motif. See README for details. * 12 * * 13 * This software is distributed in the hope that it will be useful, but WITHOUT * 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * 16 * for more details.* * 17 * * 18 * You should have received a copy of the GNU General Public License along with * 19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple * 20 * Place, Suite 330, Boston, MA 02111-1307 USA * 21 * * 22 *******************************************************************************/ 23 24 #ifdef HAVE_CONFIG_H 25 #include "../config.h" 26 #endif 27 28 #include "utils.h" 29 #include "nedit_malloc.h" 30 31 #include <stdlib.h> 32 #include <stdio.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <pwd.h> 38 39 /* just to get 'Boolean' types defined: */ 40 #include <X11/Intrinsic.h> 41 42 #ifdef HAVE_DEBUG_H 43 #include "../debug.h" 44 #endif 45 46 #define DEFAULT_NEDIT_HOME ".xnedit" 47 static char* plainFileNames[N_FILE_TYPES] = {"nedit.rc", "autoload.nm", "nedit.history", "search.history", ""}; 48 49 static void buildFilePath(char* fullPath, const char* dir, const char* file); 50 static Boolean isDir(const char* file); 51 static Boolean isRegFile(const char* file); 52 53 /* return non-NULL value for the current working directory. 54 If system call fails, provide a fallback value */ 55 const char* GetCurrentDir(void) 56 { 57 static char curdir[MAXPATHLEN]; 58 59 if (!getcwd(curdir, (size_t) MAXPATHLEN)) { 60 perror("xnedit: getcwd() fails"); 61 strcpy(curdir, "."); 62 } 63 return (curdir); 64 } 65 66 67 /* return a non-NULL value for the user's home directory, 68 without trailing slash. 69 We try the environment var and the system user database. */ 70 const char* GetHomeDir(void) 71 { 72 const char *ptr; 73 static char homedir[MAXPATHLEN]=""; 74 struct passwd *passwdEntry; 75 size_t len; 76 77 if (*homedir) { 78 return homedir; 79 } 80 ptr=getenv("HOME"); 81 if (!ptr) { 82 passwdEntry = getpwuid(getuid()); 83 if (passwdEntry && *(passwdEntry->pw_dir)) { 84 ptr= passwdEntry->pw_dir; 85 } else { 86 /* This is really serious, so just exit. */ 87 perror("xnedit: getpwuid() failed "); 88 exit(EXIT_FAILURE); 89 } 90 } 91 strncpy(homedir, ptr, sizeof(homedir)-1); 92 homedir[sizeof(homedir)-1]='\0'; 93 /* Fix trailing slash */ 94 len=strlen(homedir); 95 if (len>1 && homedir[len-1]=='/') { 96 homedir[len-1]='\0'; 97 } 98 return homedir; 99 } 100 101 /* 102 ** Return a pointer to the username of the current user in a statically 103 ** allocated string. 104 */ 105 const char 106 *GetUserName(void) 107 { 108 #ifdef VMS 109 return cuserid(NULL); 110 #else 111 /* cuserid has apparently been dropped from the ansi C standard, and if 112 strict ansi compliance is turned on (on Sun anyhow, maybe others), calls 113 to cuserid fail to compile. Older versions of nedit try to use the 114 getlogin call first, then if that fails, use getpwuid and getuid. This 115 results in the user-name of the original terminal being used, which is 116 not correct when the user uses the su command. Now, getpwuid only: */ 117 118 const struct passwd *passwdEntry; 119 static char *userName=NULL; 120 121 if (userName) 122 return userName; 123 124 passwdEntry = getpwuid(getuid()); 125 if (!passwdEntry) { 126 /* This is really serious, but sometimes username service 127 is misconfigured through no fault of the user. Be nice 128 and let the user start nc anyway. */ 129 perror("xnedit: getpwuid() failed - reverting to $USER"); 130 return getenv("USER"); 131 } 132 else { 133 userName=malloc(strlen(passwdEntry->pw_name)+1); 134 strcpy(userName, passwdEntry->pw_name); 135 return userName; 136 } 137 #endif /* VMS */ 138 } 139 140 141 /* 142 ** Writes the hostname of the current system in string "hostname". 143 ** 144 ** NOTE: This function used to be called "GetHostName" but that resulted in a 145 ** linking conflict on VMS with the standard gethostname function, because 146 ** VMS links case-insensitively. 147 */ 148 const char 149 *GetNameOfHost(void) 150 { 151 static char hostname[MAXNODENAMELEN+1]; 152 static int hostnameFound = False; 153 154 if (!hostnameFound) { 155 #ifdef VMS 156 /* This should be simple, but uname is not supported in the DEC C RTL and 157 gethostname on VMS depends either on Multinet or UCX. So use uname 158 on Unix, and use LIB$GETSYI on VMS. Note the VMS hostname will 159 be in DECNET format with trailing double colons, e.g. "FNALV1::". */ 160 int syi_status; 161 struct dsc$descriptor_s *hostnameDesc; 162 unsigned long int syiItemCode = SYI$_NODENAME; /* get Nodename */ 163 unsigned long int unused = 0; 164 unsigned short int hostnameLen = MAXNODENAMELEN+1; 165 166 hostnameDesc = NulStrWrtDesc(hostname, MAXNODENAMELEN+1); 167 syi_status = lib$getsyi(&syiItemCode, &unused, hostnameDesc, &hostnameLen, 168 0, 0); 169 if (syi_status != SS$_NORMAL) { 170 fprintf(stderr, "nedit: Error return from lib$getsyi: %d", syi_status); 171 strcpy(hostname, "VMS"); 172 } else 173 hostname[hostnameLen] = '\0'; 174 FreeStrDesc(hostnameDesc); 175 #else 176 struct utsname nameStruct; 177 int rc = uname(&nameStruct); 178 if (rc<0) { 179 /* Shouldn't ever happen, so we better exit() here */ 180 perror("xnedit: uname() failed "); 181 exit(EXIT_FAILURE); 182 } 183 strcpy(hostname, nameStruct.nodename); 184 #endif /* VMS */ 185 hostnameFound = True; 186 } 187 return hostname; 188 } 189 190 191 /* 192 ** Create a path: $HOME/filename 193 ** Return "" if it doesn't fit into the buffer 194 */ 195 char 196 *PrependHome(const char *filename, char *buf, size_t buflen) 197 { 198 const char *homedir; 199 size_t home_len, file_len; 200 201 homedir=GetHomeDir(); 202 home_len=strlen(homedir); 203 file_len=strlen(filename); 204 if ( (home_len+1+file_len)>=buflen ) { 205 buf[0]='\0'; 206 } 207 else { 208 strcpy(buf, homedir); 209 strcat(buf, "/"); 210 strcat(buf, filename); 211 } 212 return buf; 213 } 214 215 int Min(int i1, int i2) 216 { 217 return i1 <= i2 ? i1 : i2; 218 } 219 220 /* 221 ** Returns a pointer to the name of an rc file of the requested type. 222 ** 223 ** Preconditions: 224 ** - MAXPATHLEN is set to the max. allowed path length 225 ** - fullPath points to a buffer of at least MAXPATHLEN 226 ** 227 ** Returns: 228 ** - NULL if an error occurs while creating a directory 229 ** - Pointer to a static array containing the file name 230 ** 231 */ 232 const char* GetRCFileName(int type) 233 { 234 static char rcFiles[N_FILE_TYPES][MAXPATHLEN + 1]; 235 static int namesDetermined = False; 236 237 if (!namesDetermined) 238 { 239 char* nedit_home; 240 int i; 241 242 if ((nedit_home = getenv("XNEDIT_HOME")) == NULL) 243 { 244 /* ${HOME}/.nedit does not exist as a regular file. */ 245 /* FIXME: Devices, sockets and fifos are ignored for now. */ 246 char defaultNEditHome[MAXPATHLEN + 1]; 247 buildFilePath(defaultNEditHome, GetHomeDir(), DEFAULT_NEDIT_HOME); 248 if (!isDir(defaultNEditHome)) 249 { 250 /* Create DEFAULT_NEDIT_HOME */ 251 if (mkdir(defaultNEditHome, 0777) != 0) 252 { 253 perror("xnedit: Error while creating rc file directory" 254 " $HOME/" DEFAULT_NEDIT_HOME "\n" 255 " (Make sure all parent directories exist.)"); 256 return NULL; 257 } 258 } 259 260 /* All set for DEFAULT_NEDIT_HOME, let's copy the names */ 261 for (i = 0; i < N_FILE_TYPES; i++) 262 { 263 buildFilePath(rcFiles[i], defaultNEditHome, plainFileNames[i]); 264 } 265 } else 266 { 267 /* $XNEDIT_HOME is set. */ 268 #ifndef VMS 269 /* FIXME: Is this required? Does VMS know stat(), mkdir()? */ 270 if (!isDir(nedit_home)) 271 { 272 /* Create $NEDIT_HOME */ 273 if (mkdir(nedit_home, 0777) != 0) 274 { 275 perror("xnedit: Error while creating rc file directory $XNEDIT_HOME\n" 276 "xnedit: (Make sure all parent directories exist.)"); 277 return NULL; 278 } 279 } 280 #endif /* #ifndef VMS */ 281 282 /* All set for NEDIT_HOME, let's copy the names */ 283 for (i = 0; i < N_FILE_TYPES; i++) 284 { 285 buildFilePath(rcFiles[i], nedit_home, plainFileNames[i]); 286 } 287 } 288 289 namesDetermined = True; 290 } 291 292 return rcFiles[type]; 293 } 294 295 /* 296 ** Builds a file path from 'dir' and 'file', watching for buffer overruns. 297 ** 298 ** Preconditions: 299 ** - MAXPATHLEN is set to the max. allowed path length 300 ** - 'fullPath' points to a buffer of at least MAXPATHLEN 301 ** - 'dir' and 'file' are valid strings 302 ** 303 ** Postcondition: 304 ** - 'fullpath' will contain 'dir/file' 305 ** - Exits when the result would be greater than MAXPATHLEN 306 */ 307 static void buildFilePath(char* fullPath, const char* dir, const char* file) 308 { 309 if ((MAXPATHLEN) < strlen(dir) + strlen(file) + 2) 310 { 311 /* We have no way to build the path. */ 312 fprintf(stderr, "xnedit: rc file path too long for %s.\n", file); 313 exit(EXIT_FAILURE); 314 } 315 316 /* The length is already checked */ 317 strcpy(fullPath, dir); 318 #ifdef VMS 319 strcat(fullPath, ":"); 320 #else /* #ifdef VMS */ 321 strcat(fullPath, "/"); 322 #endif /* #ifdef VMS */ 323 strcat(fullPath, file); 324 } 325 326 /* 327 ** Returns true if 'file' is a directory, false otherwise. 328 ** Links are followed. 329 ** 330 ** Preconditions: 331 ** - None 332 ** 333 ** Returns: 334 ** - True for directories, false otherwise 335 */ 336 static Boolean isDir(const char* file) 337 { 338 struct stat attribute; 339 340 return ((stat(file, &attribute) == 0) && S_ISDIR(attribute.st_mode)); 341 } 342 343 /* 344 ** Returns true if 'file' is a regular file, false otherwise. 345 ** Links are followed. 346 ** 347 ** Preconditions: 348 ** - None 349 ** 350 ** Returns: 351 ** - True for regular files, false otherwise 352 */ 353 static Boolean isRegFile(const char* file) 354 { 355 struct stat attribute; 356 357 return ((stat(file, &attribute) == 0) && S_ISREG(attribute.st_mode)); 358 } 359 360 /* 361 ** Part of the simple stack. Accepts a stack and the pointer you want to 362 ** store. NULL is not allowed, as it is used in Pop() to signal an empty 363 ** stack. 364 */ 365 void Push(Stack* stack, const void* value) 366 { 367 stackObject* pushee; 368 369 /* Throw away invalid parameters. */ 370 if (NULL == value) { 371 fprintf(stderr, "xnedit: Internal error: NULL was pushed.\n"); 372 return; 373 } 374 if (NULL == stack) { 375 fprintf(stderr, "xnedit: Internal error: push() called with NULL stack.\n"); 376 return; 377 } 378 379 /* Allocate memory for new value. */ 380 pushee = (stackObject*) NEditMalloc(sizeof(stackObject)); 381 382 /* Put pushee on top of stack. */ 383 pushee->value = (void*) value; 384 pushee->next = stack->top; 385 stack->top = pushee; 386 (stack->size)++; 387 388 return; 389 } 390 391 /* 392 ** Part of the simple stack, returns the topmost item from the stack or 393 ** NULL if the stack is empty. It also returns NULL if the stack itself 394 ** is NULL. 395 ** 396 ** Precondition: The stack's top element is either NULL or a properly 397 ** initialised stackObject. 398 */ 399 void* Pop(Stack* stack) 400 { 401 stackObject* popee; 402 void* value; 403 404 /* Throw away invalid parameter. */ 405 if (NULL == stack) { 406 fprintf(stderr, "xnedit: Internal error: pop() called with NULL stack.\n"); 407 return NULL; 408 } 409 410 /* Return NULL if Stack is empty. */ 411 if (NULL == stack->top) { 412 return NULL; 413 } 414 415 /* Remove top entry in the stack. */ 416 popee = stack->top; 417 stack->top = popee->next; 418 (stack->size)--; 419 420 value = popee->value; 421 NEditFree((char*) popee); 422 423 return value; 424 } 425 426 /* 427 ** We currently don't need this function: In the only situation where we use 428 ** the stack, we empty it completely. This might come in handy if the stack 429 ** is ever used anywhere else. 430 ** 431 ** Beware: Utterly untested. 432 */ /* 433 void FreeStack(Stack* stack) 434 { 435 void* dummy; 436 437 while (NULL != (dummy = pop(progStack))) {} 438 439 NEditFree((char*) stack); 440 } 441 */ 442