util-warnings-cleanup-20010705
[openafs.git] / src / util / dirpath.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <stddef.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <limits.h>
16 #include <errno.h>
17 #include <stdio.h>
18 #include "assert.h"
19 #include "afsutil.h"
20 #include "fileutil.h"
21 #ifdef AFS_PTHREAD_ENV
22 #include <pthread.h>
23 static pthread_once_t dirInit_once = PTHREAD_ONCE_INIT;
24 #endif
25 #ifdef AFS_NT40_ENV
26 #include <windows.h>
27 #include <WINNT\afssw.h>
28 #endif
29 #ifdef AFS_DARWIN_ENV
30 #include <unistd.h>
31 #endif
32
33 /* local vars */
34 /* static storage for path strings */
35 static char dirPathArray[AFSDIR_PATHSTRING_MAX][AFSDIR_PATH_MAX];
36
37 /* indicate if and how the dirpath module initialized. */
38 static int initFlag = 0;
39 static unsigned int initStatus = 0;
40
41
42 /* storage for dynamically-determined install dir (NT only; long and short) */
43 #ifdef AFS_NT40_ENV
44 static char ntServerInstallDirLong[AFSDIR_PATH_MAX];
45 static char ntServerInstallDirShort[AFSDIR_PATH_MAX];
46 static char ntClientConfigDirLong[AFSDIR_PATH_MAX];
47 static char ntClientConfigDirShort[AFSDIR_PATH_MAX];
48 #endif
49
50 /* storage for local afs server/client paths (top-level) */
51 static char afsSrvDirPath[AFSDIR_PATH_MAX];
52 static char afsClntDirPath[AFSDIR_PATH_MAX];
53
54 /* internal array init function */
55 static void initDirPathArray(void);
56
57 /* Additional macros for ease of use */
58 /* buf is expected to be atleast AFS_PATH_MAX bytes long */
59 #define AFSDIR_SERVER_DIRPATH(buf, dir)  \
60             strcompose(buf, AFSDIR_PATH_MAX, dirPathArray[AFSDIR_SERVER_AFS_DIRPATH_ID], "/", dir, NULL)
61
62 #define AFSDIR_SERVER_FILEPATH(buf, dir, file)  \
63             strcompose(buf, AFSDIR_PATH_MAX, dirPathArray[AFSDIR_SERVER_AFS_DIRPATH_ID], "/", dir, "/", file,  NULL)
64
65 #define AFSDIR_CLIENT_DIRPATH(buf, dir)  \
66             strcompose(buf, AFSDIR_PATH_MAX, dirPathArray[AFSDIR_CLIENT_VICE_DIRPATH_ID], "/", dir, NULL)
67
68 #define AFSDIR_CLIENT_FILEPATH(buf, dir, file)  \
69             strcompose(buf, AFSDIR_PATH_MAX, dirPathArray[AFSDIR_CLIENT_VICE_DIRPATH_ID], "/", dir, "/", file,  NULL)
70
71
72 /* initAFSDirPath() -- External users call this function to initialize
73  * the dirpath module and/or to determine the initialization status.
74  */
75 unsigned int initAFSDirPath(void)
76 {
77     if (initFlag == 0) { /* not yet init'ed, so initialize */
78 #ifdef AFS_PTHREAD_ENV
79         pthread_once(&dirInit_once, initDirPathArray);
80 #else
81         initDirPathArray();
82 #endif
83     }
84     return initStatus;
85 }
86
87
88 /* initDirPathArray() -- Initializes the afs dir paths for the 
89  *     server and client installations.
90  *
91  *     For NT these are determined dynamically; for Unix they are static.
92  *
93  *     NT NOTE: If a particular component (client/server) is not installed
94  *              then we may not be able to initialize the paths to anything
95  *              meaningful.  In this case the paths are set to the local
96  *              temp directory to avoid later reference to an uninitialized
97  *              variable.  The initStatus flag is set to indicate which
98  *              paths (client/server) initialized properly for callers of
99  *              initAFSDirPath() who would like to know this information.
100  */
101 static void initDirPathArray(void)
102
103     char *pathp;
104
105 #ifdef AFS_NT40_ENV
106     char *buf;
107     int status;
108
109     /* get the afs server software installation dir from the registry */
110     if (afssw_GetServerInstallDir(&buf)) {
111         /* failed; server not installed; use temp directory */
112         strcpy(ntServerInstallDirLong, gettmpdir());
113     } else {
114         strcpy(ntServerInstallDirLong, buf);
115         free(buf);
116         initStatus |= AFSDIR_SERVER_PATHS_OK;
117     }
118     FilepathNormalize(ntServerInstallDirLong);
119     status = GetShortPathName(ntServerInstallDirLong,
120                               ntServerInstallDirShort, AFSDIR_PATH_MAX);
121     if (status == 0 || status > AFSDIR_PATH_MAX) {
122         /* can't convert path to short version; just use long version */
123         strcpy(ntServerInstallDirShort, ntServerInstallDirLong);
124     }
125     FilepathNormalize(ntServerInstallDirShort);
126
127     /* get the afs client configuration directory (/usr/vice/etc equivalent) */
128     status = GetWindowsDirectory(ntClientConfigDirLong, AFSDIR_PATH_MAX);
129     if (status == 0 || status > AFSDIR_PATH_MAX) {
130         /* failed to get canonical Windows directory; use temp directory */
131         strcpy(ntClientConfigDirLong, gettmpdir());
132     } else {
133         initStatus |= AFSDIR_CLIENT_PATHS_OK;
134     }
135     FilepathNormalize(ntClientConfigDirLong);
136
137     status = GetShortPathName(ntClientConfigDirLong,
138                               ntClientConfigDirShort, AFSDIR_PATH_MAX);
139     if (status == 0 || status > AFSDIR_PATH_MAX) {
140         /* can't convert path to short version; just use long version */
141         strcpy(ntClientConfigDirShort, ntClientConfigDirLong);
142     }
143     FilepathNormalize(ntClientConfigDirShort);
144
145     /* setup the root server directory path (/usr/afs equivalent) */
146     strcpy(afsSrvDirPath, ntServerInstallDirShort);
147     strcat(afsSrvDirPath, AFSDIR_CANONICAL_SERVER_AFS_DIRPATH);
148
149     /* there is no root client directory path (/usr/vice equivalent) */
150     afsClntDirPath[0] = '\0';
151
152     /* setup top level dirpath (/usr equivalent); valid for server ONLY */
153     strcpy(dirPathArray[AFSDIR_USR_DIRPATH_ID], ntServerInstallDirShort);
154     strcat(dirPathArray[AFSDIR_USR_DIRPATH_ID], AFSDIR_CANONICAL_USR_DIRPATH);
155
156 #else /* AFS_NT40_ENV */
157     /* setup the root server directory path */
158     strcpy(afsSrvDirPath, AFSDIR_CANONICAL_SERVER_AFS_DIRPATH);
159
160     /* setup the root client directory path */
161 #ifdef AFS_DARWIN_ENV
162     if (access(AFSDIR_ALTERNATE_CLIENT_VICE_DIRPATH, F_OK) == 0)
163         strcpy(afsClntDirPath, AFSDIR_ALTERNATE_CLIENT_VICE_DIRPATH);
164     else 
165 #endif
166     strcpy(afsClntDirPath, AFSDIR_CANONICAL_CLIENT_VICE_DIRPATH);
167
168     /* setup top level dirpath; valid for both server and client */
169     strcpy(dirPathArray[AFSDIR_USR_DIRPATH_ID], AFSDIR_CANONICAL_USR_DIRPATH);
170
171     initStatus |= (AFSDIR_CLIENT_PATHS_OK | AFSDIR_SERVER_PATHS_OK);
172 #endif /* AFS_NT40_ENV */
173
174   /* now initialize various dir and file paths exported by dirpath module */
175
176   /* server dir paths */
177  
178   strcpy(dirPathArray[AFSDIR_SERVER_AFS_DIRPATH_ID], afsSrvDirPath);
179
180   pathp = dirPathArray[AFSDIR_SERVER_ETC_DIRPATH_ID];
181   AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_ETC_DIR);
182
183   pathp = dirPathArray[AFSDIR_SERVER_BIN_DIRPATH_ID];
184   AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_BIN_DIR);
185
186   pathp = dirPathArray[AFSDIR_SERVER_CORES_DIRPATH_ID];
187   AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_CORES_DIR);
188
189   pathp = dirPathArray[AFSDIR_SERVER_DB_DIRPATH_ID];
190   AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_DB_DIR);
191
192   pathp = dirPathArray[AFSDIR_SERVER_LOGS_DIRPATH_ID];
193   AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_LOGS_DIR);
194
195   pathp = dirPathArray[AFSDIR_SERVER_LOCAL_DIRPATH_ID];
196   AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_LOCAL_DIR);
197
198   pathp = dirPathArray[AFSDIR_SERVER_BACKUP_DIRPATH_ID];
199   AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_BACKUP_DIR);
200
201   pathp = dirPathArray[AFSDIR_SERVER_MIGRATE_DIRPATH_ID];
202   AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_MIGR_DIR);
203
204   pathp = dirPathArray[AFSDIR_SERVER_BIN_FILE_DIRPATH_ID];
205   AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_BIN_FILE_DIR);
206
207   /* client dir path */
208
209 #ifdef AFS_NT40_ENV
210   strcpy(dirPathArray[AFSDIR_CLIENT_VICE_DIRPATH_ID],
211          "/NoUsrViceDirectoryOnWindows");
212   strcpy(dirPathArray[AFSDIR_CLIENT_ETC_DIRPATH_ID], ntClientConfigDirShort);
213 #else
214   strcpy(dirPathArray[AFSDIR_CLIENT_VICE_DIRPATH_ID], afsClntDirPath);
215
216   pathp = dirPathArray[AFSDIR_CLIENT_ETC_DIRPATH_ID];
217   AFSDIR_CLIENT_DIRPATH(pathp, AFSDIR_ETC_DIR);
218 #endif /* AFS_NT40_ENV */
219
220   /* server file paths */
221
222   pathp = dirPathArray[AFSDIR_SERVER_THISCELL_FILEPATH_ID];
223   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_ETC_DIR, AFSDIR_THISCELL_FILE);
224
225   pathp = dirPathArray[AFSDIR_SERVER_CELLSERVDB_FILEPATH_ID];
226   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_ETC_DIR, AFSDIR_CELLSERVDB_FILE);
227
228   pathp = dirPathArray[AFSDIR_SERVER_NOAUTH_FILEPATH_ID];
229   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_NOAUTH_FILE);
230
231   pathp = dirPathArray[AFSDIR_SERVER_BUDBLOG_FILEPATH_ID];
232   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_BUDBLOG_FILE);
233
234   pathp = dirPathArray[AFSDIR_SERVER_TAPECONFIG_FILEPATH_ID];
235   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_BACKUP_DIR, AFSDIR_TAPECONFIG_FILE);
236
237   pathp = dirPathArray[AFSDIR_SERVER_KALOGDB_FILEPATH_ID];
238   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_KALOGDB_FILE);
239
240   pathp = dirPathArray[AFSDIR_SERVER_KALOG_FILEPATH_ID];
241   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_KALOG_FILE);
242
243   pathp = dirPathArray[AFSDIR_SERVER_KADB_FILEPATH_ID];
244   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_DB_DIR, AFSDIR_KADB_FILE);
245
246   pathp = dirPathArray[AFSDIR_SERVER_NTPD_FILEPATH_ID];
247   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_BIN_DIR, AFSDIR_NTPD_FILE);
248
249   pathp = dirPathArray[AFSDIR_SERVER_PRDB_FILEPATH_ID];
250   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_DB_DIR, AFSDIR_PRDB_FILE);
251
252   pathp = dirPathArray[AFSDIR_SERVER_PTLOG_FILEPATH_ID];
253   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_PTLOG_FILE);
254
255   pathp = dirPathArray[AFSDIR_SERVER_KCONF_FILEPATH_ID];
256   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_ETC_DIR, AFSDIR_KCONF_FILE);
257
258   pathp = dirPathArray[AFSDIR_SERVER_VLDB_FILEPATH_ID];
259   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_DB_DIR, AFSDIR_VLDB_FILE);
260
261   pathp = dirPathArray[AFSDIR_SERVER_VLOG_FILEPATH_ID];
262   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_VLOG_FILE);
263
264   pathp = dirPathArray[AFSDIR_SERVER_CORELOG_FILEPATH_ID];
265   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_CORE_FILE);
266
267   pathp = dirPathArray[AFSDIR_SERVER_SLVGLOG_FILEPATH_ID];
268   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_SLVGLOG_FILE);
269
270   pathp = dirPathArray[AFSDIR_SERVER_SALVAGER_FILEPATH_ID];
271   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_BIN_DIR, AFSDIR_SALVAGER_FILE);
272
273   pathp = dirPathArray[AFSDIR_SERVER_SLVGLOCK_FILEPATH_ID];
274   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_SLVGLOCK_FILE);
275
276   pathp = dirPathArray[AFSDIR_SERVER_KEY_FILEPATH_ID];
277   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_ETC_DIR, AFSDIR_KEY_FILE);
278
279   pathp = dirPathArray[AFSDIR_SERVER_ULIST_FILEPATH_ID];
280   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_ETC_DIR, AFSDIR_ULIST_FILE);
281
282   pathp = dirPathArray[AFSDIR_SERVER_BOZCONF_FILEPATH_ID];
283   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_BOZCONF_FILE);
284
285   pathp = dirPathArray[AFSDIR_SERVER_BOZCONFNEW_FILEPATH_ID];
286   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_BOZCONFNEW_FILE);
287
288   pathp = dirPathArray[AFSDIR_SERVER_BOZLOG_FILEPATH_ID];
289   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_BOZLOG_FILE);
290
291   pathp = dirPathArray[AFSDIR_SERVER_BOZINIT_FILEPATH_ID];
292   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_BOZINIT_FILE);
293
294   pathp = dirPathArray[AFSDIR_SERVER_BOSVR_FILEPATH_ID];
295   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_BIN_DIR, AFSDIR_BOSVR_FILE);
296
297   pathp = dirPathArray[AFSDIR_SERVER_VOLSERLOG_FILEPATH_ID];
298   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_VOLSERLOG_FILE);
299
300   pathp = dirPathArray[AFSDIR_SERVER_ROOTVOL_FILEPATH_ID];
301   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_ETC_DIR, AFSDIR_ROOTVOL_FILE);
302
303   pathp = dirPathArray[AFSDIR_SERVER_HOSTDUMP_FILEPATH_ID];
304   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_HOSTDUMP_FILE);
305
306   pathp = dirPathArray[AFSDIR_SERVER_CLNTDUMP_FILEPATH_ID];
307   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_CLNTDUMP_FILE);
308
309   pathp = dirPathArray[AFSDIR_SERVER_CBKDUMP_FILEPATH_ID];
310   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_CBKDUMP_FILE);
311
312   pathp = dirPathArray[AFSDIR_SERVER_OLDSYSID_FILEPATH_ID];
313   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_OLDSYSID_FILE);
314
315   pathp = dirPathArray[AFSDIR_SERVER_SYSID_FILEPATH_ID];
316   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_SYSID_FILE);
317
318   pathp = dirPathArray[AFSDIR_SERVER_FILELOG_FILEPATH_ID];
319   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_FILELOG_FILE);
320
321   pathp = dirPathArray[AFSDIR_SERVER_AUDIT_FILEPATH_ID];
322   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_AUDIT_FILE);
323
324   pathp = dirPathArray[AFSDIR_SERVER_NETINFO_FILEPATH_ID];
325   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_NETINFO_FILE);
326
327   pathp = dirPathArray[AFSDIR_SERVER_NETRESTRICT_FILEPATH_ID];
328   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_NETRESTRICT_FILE);
329
330   pathp = dirPathArray[AFSDIR_SERVER_WEIGHTING_CONSTANTS_FILEPATH_ID];
331   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_MIGR_DIR, AFSDIR_WEIGHTINGCONST_FILE);
332
333   pathp = dirPathArray[AFSDIR_SERVER_THRESHOLD_CONSTANTS_FILEPATH_ID];
334   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_MIGR_DIR, AFSDIR_THRESHOLDCONST_FILE);
335
336   pathp = dirPathArray[AFSDIR_SERVER_MIGRATELOG_FILEPATH_ID];
337   AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_MIGR_DIR, AFSDIR_MIGRATE_LOGNAME);
338  
339
340   /* client file paths */
341
342 #ifdef AFS_NT40_ENV
343   strcpy(dirPathArray[AFSDIR_CLIENT_THISCELL_FILEPATH_ID],
344          "/NoUsrViceEtcThisCellFileOnWindows");
345   sprintf(dirPathArray[AFSDIR_CLIENT_CELLSERVDB_FILEPATH_ID], "%s/%s",
346           ntClientConfigDirShort, AFSDIR_CELLSERVDB_FILE_NTCLIENT);
347 #else
348   pathp = dirPathArray[AFSDIR_CLIENT_THISCELL_FILEPATH_ID];
349   AFSDIR_CLIENT_FILEPATH(pathp, AFSDIR_ETC_DIR, AFSDIR_THISCELL_FILE);
350
351   pathp = dirPathArray[AFSDIR_CLIENT_CELLSERVDB_FILEPATH_ID]; 
352   AFSDIR_CLIENT_FILEPATH(pathp, AFSDIR_ETC_DIR, AFSDIR_CELLSERVDB_FILE);
353 #endif /* AFS_NT40_ENV */
354
355   pathp = dirPathArray[AFSDIR_CLIENT_NETINFO_FILEPATH_ID];
356   AFSDIR_CLIENT_FILEPATH(pathp, AFSDIR_ETC_DIR, AFSDIR_NETINFO_FILE);
357
358   pathp = dirPathArray[AFSDIR_CLIENT_NETRESTRICT_FILEPATH_ID];
359   AFSDIR_CLIENT_FILEPATH(pathp, AFSDIR_ETC_DIR, AFSDIR_NETRESTRICT_FILE);
360
361   initFlag = 1;  /* finished dirpath initialization */
362   return;
363 }
364
365 /* getDirPath - returns a const char pointer to the requested string
366  * from the internal path array.
367  * string_id - index into the path array 
368  */
369 const char *getDirPath(afsdir_id_t string_id)
370 {
371     /* check if the array has been initialized */
372     if (initFlag == 0) { /* no it's not, so initialize */
373 #ifdef AFS_PTHREAD_ENV
374         pthread_once(&dirInit_once, initDirPathArray);
375 #else
376         initDirPathArray();
377 #endif
378     }
379     return (const char *)dirPathArray[string_id];
380 }
381
382
383 #ifdef AFS_NT40_ENV
384 /* NT version of ConstructLocalPath() */
385
386 /*
387  * ConstructLocalPath() --  Convert a canonical (wire-format) path to a fully
388  *     specified local path.  Upon successful completion, *fullPathBufp is
389  *     set to an allocated buffer containing the fully specified local path
390  *     constructed from the cpath argument.
391  *
392  *     On NT, path construction proceeds as follows:
393  *         1) If cpath is fully qualified (i.e., starts with 'X:/') then the
394  *            path returned is equivalent to cpath.
395  *         2) If cpath begins with a drive letter but is not fully qualified,
396  *            i.e., it is drive relative, then the function fails with EINVAL.
397  *         3) If cpath begins with '/' (or '\') then the path returned is the
398  *            concatenation  AFS-server-install-dir + cpath.
399  *         4) Otherwise the path returned is the concatenation
400  *            AFS-server-install-dir + relativeTo + cpath.
401  *
402  *     Leading whitespace in cpath is ignored; the constructed path is
403  *     normalized (FilepathNormalize()).
404  *
405  * RETURN CODES: 0 if successful; errno code otherwise.
406  */
407 int
408 ConstructLocalPath(const char *cpath,
409                    const char *relativeTo,
410                    char **fullPathBufp)
411 {
412     int status = 0;
413     char *newPath = NULL;
414
415     if (initFlag == 0) { /* dirpath module not yet initialized */
416 #ifdef AFS_PTHREAD_ENV
417         pthread_once(&dirInit_once, initDirPathArray);
418 #else
419         initDirPathArray();
420 #endif
421     }
422
423     *fullPathBufp = NULL;
424
425     while (isspace(*cpath)) {
426         cpath++;
427     }
428
429     if ((((*cpath >= 'a') && (*cpath <= 'z')) ||
430          ((*cpath >= 'A') && (*cpath <= 'Z'))) &&
431         (*(cpath+1) == ':')) {
432
433         /* cpath has a leading drive letter */
434         if ((*(cpath+2) != '/') && (*(cpath+2) != '\\')) {
435             /* drive letter relative path; this is not allowed */
436             status = EINVAL;
437         } else {
438             /* fully qualified path; just make a copy */
439             newPath = (char *)malloc(strlen(cpath)+1);
440             if (!newPath) {
441                 status = ENOMEM;
442             } else {
443                 (void)strcpy(newPath, cpath);
444             }
445         }
446
447     } else {
448         /* cpath has NO leading drive letter; make relative to install dir */
449         size_t pathSize = strlen(ntServerInstallDirShort) + 2;
450
451         if ((*cpath == '/') || (*cpath == '\\')) {
452             /* construct path relative to install directory only */
453             pathSize += strlen(cpath);
454
455             newPath = (char *)malloc(pathSize);
456             if (!newPath) {
457                 status = ENOMEM;
458             } else {
459                 sprintf(newPath, "%s/%s", ntServerInstallDirShort, cpath);
460             }
461         } else {
462             /* construct path relative to 'relativeTo' (and install dir) */
463             pathSize += strlen(relativeTo) + 1 + strlen(cpath);
464
465             newPath = (char *)malloc(pathSize);
466             if (!newPath) {
467                 status = ENOMEM;
468             } else {
469                 sprintf(newPath, "%s/%s/%s",
470                         ntServerInstallDirShort, relativeTo, cpath);
471             }
472         }
473     }
474
475     if (status == 0) {
476         FilepathNormalize(newPath);
477
478         /* return buffer containing fully specified path */
479         *fullPathBufp = newPath;
480     }
481
482     return status;
483 }
484
485 #else
486 /* Unix version of ConstructLocalPath() */
487
488 /*
489  * ConstructLocalPath() --  Convert a canonical (wire-format) path to a fully
490  *     specified local path.  Upon successful completion, *fullPathBufp is
491  *     set to an allocated buffer containing the fully specified local path
492  *     constructed from the cpath argument.
493  *
494  *     On Unix, path construction proceeds as follows:
495  *         1) If cpath begins with '/' then the path returned is equivalent
496  *            to cpath.
497  *         2) Otherwise the path returned is the concatenation
498  *            relativeTo + cpath.
499  *
500  *     Leading whitespace in cpath is ignored; the constructed path is
501  *     normalized (FilepathNormalize()).
502  *
503  * RETURN CODES: 0 if successful; errno code otherwise.
504  */
505 int
506 ConstructLocalPath(const char *cpath,
507                    const char *relativeTo,
508                    char **fullPathBufp)
509 {
510     int status = 0;
511     char *newPath = NULL;
512
513     if (initFlag == 0) { /* dirpath module not yet initialized */
514 #ifdef AFS_PTHREAD_ENV
515         pthread_once(&dirInit_once, initDirPathArray);
516 #else
517         initDirPathArray();
518 #endif
519     }
520
521     *fullPathBufp = NULL;
522
523     while (isspace(*cpath)) {
524         cpath++;
525     }
526
527     if (*cpath == '/') {
528         newPath = (char *)malloc(strlen(cpath) + 1);
529         if (!newPath) {
530             status = ENOMEM;
531         } else {
532             strcpy(newPath, cpath);
533         }
534     } else {
535         newPath = (char *)malloc(strlen(relativeTo) + 1 + strlen(cpath) + 1);
536         if (!newPath) {
537             status = ENOMEM;
538         } else {
539             sprintf(newPath, "%s/%s", relativeTo, cpath);
540         }
541     }
542
543     if (status == 0) {
544         FilepathNormalize(newPath);
545
546         /* return buffer containing fully specified path */
547         *fullPathBufp = newPath;
548     }
549
550     return status;
551 }
552 #endif /* AFS_NT40_ENV */
553
554
555 /*
556  * ConstructLocalBinPath() -- A convenience wrapper for ConstructLocalPath()
557  *     that specifies the canonical AFS server binary directory as the relative
558  *     directory.
559  */
560 int
561 ConstructLocalBinPath(const char *cpath,
562                       char **fullPathBufp)
563 {
564     return ConstructLocalPath(cpath,
565                               AFSDIR_CANONICAL_SERVER_BIN_DIRPATH,
566                               fullPathBufp);
567 }
568
569
570 /*
571  * ConstructLocalLogPath() -- A convenience wrapper for ConstructLocalPath()
572  *     that specifies the canonical AFS server logs directory as the relative
573  *     directory.
574  */
575 int
576 ConstructLocalLogPath(const char *cpath,
577                       char **fullPathBufp)
578 {
579     return ConstructLocalPath(cpath,
580                               AFSDIR_CANONICAL_SERVER_LOGS_DIRPATH,
581                               fullPathBufp);
582 }