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