no-more-ini-files-20040713
[openafs.git] / src / WINNT / afsd / cm_config.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 <afs/stds.h>
12 #include <afs/cellconfig.h>
13
14 #ifndef DJGPP
15 #include <windows.h>
16 #include <winsock2.h>
17 #else
18 #include <sys/socket.h>
19 #include <netdb.h>
20 #endif /* !DJGPP */
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 #include "cm_config.h"
26 #include <WINNT\afssw.h>
27 #ifdef AFS_AFSDB_ENV
28 #include "cm_dns.h"
29 #include <afs/afsint.h>
30 #endif
31
32 char AFSConfigKeyName[] =
33         "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters";
34
35 /* TODO: these should be pulled in from dirpath.h */
36 #if !defined(DJGPP) && !defined(AFS_WIN95_ENV)
37 #define AFS_THISCELL "ThisCell"
38 #endif
39 #define AFS_CELLSERVDB_UNIX "CellServDB"
40 #define AFS_CELLSERVDB_NT "afsdcell.ini"
41 #ifndef AFSDIR_CLIENT_ETC_DIRPATH
42 #define AFSDIR_CLIENT_ETC_DIRPATH "c:/afs"
43 #endif
44 #if defined(DJGPP) || defined(AFS_WIN95_ENV)
45 #define AFS_CELLSERVDB AFS_CELLSERVDB_UNIX
46 #ifdef DJGPP
47 extern char cm_confDir[];
48 extern int errno;
49 #endif /* DJGPP */
50 #else
51 #define AFS_CELLSERVDB AFS_CELLSERVDB_UNIX
52 #endif /* DJGPP || WIN95 */
53
54 #ifdef DEBUG
55 DWORD TraceOption=1;
56
57 #define TRACE_OPTION_EVENT 1
58 #define ISLOGONTRACE(v) ( ((v) & TRACE_OPTION_EVENT)==TRACE_OPTION_EVENT)
59
60 void DebugEvent0_local(char *a) 
61 {
62         HANDLE h; char *ptbuf[1];
63         if (!ISLOGONTRACE(TraceOption))
64                 return;
65         h = RegisterEventSource(NULL, a);
66         ptbuf[0] = a;
67         ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);
68         DeregisterEventSource(h);
69 }
70
71 #define MAXBUF_ 512
72
73 void DebugEvent_local(char *a,char *b,...) 
74 {
75         HANDLE h; char *ptbuf[1],buf[MAXBUF_+1];
76         va_list marker;
77         if (!ISLOGONTRACE(TraceOption))
78                 return;
79         h = RegisterEventSource(NULL, a);
80         va_start(marker,b);
81         _vsnprintf(buf,MAXBUF_,b,marker);
82         ptbuf[0] = buf;
83         ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);\
84         DeregisterEventSource(h);
85         va_end(marker);
86 }
87 #endif /* DEBUG */
88
89 static long cm_ParsePair(char *lineBufferp, char *leftp, char *rightp)
90 {
91         char *tp;
92     char tc;
93     int sawEquals;
94         int sawBracket;
95         
96     sawEquals = 0;
97         sawBracket = 0;
98     for(tp = lineBufferp; *tp; tp++) {
99         tc = *tp;
100
101                 if (sawBracket) {
102                         if (tc == ']')
103                                 sawBracket = 0;
104                         continue;
105                 }
106
107                 /* comment or line end */
108         if (tc == '#' || tc == '\r' || tc == '\n') 
109             break;
110
111                 /* square bracket comment -- look for closing delim */
112                 if (tc == '[') {
113             sawBracket = 1; 
114             continue;
115         }
116
117                 /* space or tab */
118         if (tc == ' ' || tc == '\t') 
119             continue;
120
121         if (tc == '=') {
122             sawEquals = 1;
123             continue;
124                 }
125                 
126         /* now we have a real character, put it in the appropriate bucket */
127         if (sawEquals == 0) {
128                         *leftp++ = tc;
129         }
130         else {
131                         *rightp++ = tc;
132         }
133     }
134
135         /* null terminate the strings */
136         *leftp = 0;
137     *rightp = 0;
138
139     return 0;   /* and return success */
140 }
141
142 /* search for a cell, and either return an error code if we don't find it,
143  * or return 0 if we do, in which case we also fill in the addresses in
144  * the cellp field.
145  *
146  * new feature:  we can handle abbreviations and are insensitive to case.
147  * If the caller wants the "real" cell name, it puts a non-null pointer in
148  * newCellNamep.  Anomaly:  if cellNamep is ambiguous, we may modify
149  * newCellNamep but return an error code.
150  */
151 long cm_SearchCellFile(char *cellNamep, char *newCellNamep,
152         cm_configProc_t *procp, void *rockp)
153 {
154         char wdir[257];
155     int tlen;
156     FILE *tfilep = NULL, *bestp, *tempp;
157     char *tp;
158     char lineBuffer[257];
159     struct hostent *thp;
160     char *valuep;
161     struct sockaddr_in vlSockAddr;
162     int inRightCell;
163     int foundCell = 0;
164     long code;
165         int tracking = 1, partial = 0;
166 #if defined(DJGPP) || defined(AFS_WIN95_ENV)
167     char *afsconf_path;
168 #endif
169
170 #if !defined(DJGPP)
171     strcpy(wdir, AFSDIR_CLIENT_ETC_DIRPATH);
172
173     /* add trailing backslash, if required */
174     tlen = strlen(wdir);
175     if (wdir[tlen-1] != '\\') strcat(wdir, "\\");
176 #else
177     strcpy(wdir, cm_confDir);
178     strcat(wdir,"/");
179 #endif /* !DJGPP */
180         
181     strcat(wdir, AFS_CELLSERVDB);
182     tfilep = fopen(wdir, "r");
183
184 #if defined(DJGPP) || defined(AFS_WIN95_ENV)
185     if (!tfilep) {
186         /* If we are using DJGPP client, cellservdb will be in afsconf dir. */
187         /* If we are in Win95 here, we are linking with klog etc. and are
188         using DJGPP client even though DJGPP is not defined.  So we still
189         need to check AFSCONF for location. */
190         afsconf_path = getenv("AFSCONF");
191         if (!afsconf_path)
192             strcpy(wdir, AFSDIR_CLIENT_ETC_DIRPATH);
193         else
194             strcpy(wdir, afsconf_path);
195         strcat(wdir, "/");
196         strcat(wdir, AFS_CELLSERVDB);
197         /*fprintf(stderr, "opening cellservdb file %s\n", wdir);*/
198         tfilep = fopen(wdir, "r");
199         if (!tfilep) return -2;
200     }
201 #else
202     /* If we are NT or higher, we don't do DJGPP, So just fail */
203     if ( !tfilep )
204         return -2;
205 #endif
206
207         bestp = fopen(wdir, "r");
208     
209 #ifdef DEBUG
210     DebugEvent_local("AFS- cm_searchfile fopen", "Handle[%x], wdir[%s]", bestp, wdir);
211 #endif
212
213         /* have we seen the cell line for the guy we're looking for? */
214         inRightCell = 0;
215         while (1) {
216         tp = fgets(lineBuffer, sizeof(lineBuffer), tfilep);
217         if (tracking)
218                         (void) fgets(lineBuffer, sizeof(lineBuffer), bestp);
219         if (tp == NULL) {
220                         if (feof(tfilep)) {
221                                 /* hit EOF */
222                                 if (partial) {
223                                         /*
224                                          * found partial match earlier;
225                                          * now go back to it
226                                          */
227                                         tempp = bestp;
228                                         bestp = tfilep;
229                                         tfilep = tempp;
230                                         inRightCell = 1;
231                                         partial = 0;
232                                         continue;
233                                 }
234                                 else {
235                                         fclose(tfilep);
236                                         fclose(bestp);
237                                         return (foundCell? 0 : -3);
238                                 }
239                         }
240         }
241
242         /* turn trailing cr or lf into null */
243         tp = strchr(lineBuffer, '\r');
244         if (tp) *tp = 0;
245         tp = strchr(lineBuffer, '\n');
246         if (tp) *tp = 0;
247
248                 /* skip blank lines */
249         if (lineBuffer[0] == 0) continue;
250
251         if (lineBuffer[0] == '>') {
252                         /* trim off at white space or '#' chars */
253             tp = strchr(lineBuffer, ' ');
254             if (tp) *tp = 0;
255             tp = strchr(lineBuffer, '\t');
256             if (tp) *tp = 0;
257             tp = strchr(lineBuffer, '#');
258             if (tp) *tp = 0;
259
260                         /* now see if this is the right cell */
261             if (stricmp(lineBuffer+1, cellNamep) == 0) {
262                                 /* found the cell we're looking for */
263                                 if (newCellNamep)
264                                         strcpy(newCellNamep, lineBuffer+1);
265                 inRightCell = 1;
266                                 tracking = 0;
267 #ifdef DEBUG
268                 DebugEvent_local("AFS- cm_searchfile is cell", "inRightCell[%x], linebuffer[%s]", 
269                                  inRightCell, lineBuffer);
270 #endif
271                         }
272                         else if (strnicmp(lineBuffer+1, cellNamep,
273                                strlen(cellNamep)) == 0) {
274                                 /* partial match */
275                                 if (partial) {  /* ambiguous */
276                                         fclose(tfilep);
277                                         fclose(bestp);
278                                         return -5;
279                                 }
280                                 if (newCellNamep)
281                                         strcpy(newCellNamep, lineBuffer+1);
282                                 inRightCell = 0;
283                                 tracking = 0;
284                                 partial = 1;
285                         }
286             else inRightCell = 0;
287         }
288         else {
289 #if !defined(DJGPP) && !defined(AFS_WIN95_ENV)
290             valuep = strchr(lineBuffer, '#');
291                         if (valuep == NULL) {
292                                 fclose(tfilep);
293                                 fclose(bestp);
294                                 return -4;
295                         }
296             valuep++;   /* skip the "#" */
297
298             valuep += strspn(valuep, " \t"); /* skip SP & TAB */
299             /* strip spaces and tabs in the end. They should not be there according to CellServDB format
300             so do this just in case                        */
301             while (valuep[strlen(valuep) - 1] == ' ' || valuep[strlen(valuep) - 1] == '\t') 
302                 valuep[strlen(valuep) - 1] = '\0';
303
304 #endif /* !DJGPP */
305                         if (inRightCell) {
306 #if !defined(DJGPP) && !defined(AFS_WIN95_ENV)
307                                 /* add the server to the VLDB list */
308                 WSASetLastError(0);
309                 thp = gethostbyname(valuep);
310 #ifdef DEBUG
311                 {
312                     int iErr = WSAGetLastError();
313                     DebugEvent_local("AFS- cm_searchfile inRightCell", 
314                                      "thp[%x], valuep[%s], WSAGetLastError[%d]", 
315                                      thp, valuep, iErr);
316                 }
317 #endif
318                 if (thp) {
319                                         memcpy(&vlSockAddr.sin_addr.s_addr, thp->h_addr,
320                             sizeof(long));
321                     vlSockAddr.sin_family = AF_INET;
322                     /* sin_port supplied by connection code */
323                                         if (procp)
324                                                 (*procp)(rockp, &vlSockAddr, valuep);
325                     foundCell = 1;
326                                 }
327 #else
328                 thp = 0;
329 #endif /* !DJGPP */
330                 if (!thp) {
331                     long ip_addr;
332                                         int c1, c2, c3, c4;
333                                         char aname[241] = "";                    
334                     
335                     /* Since there is no gethostbyname() data 
336                      * available we will read the IP address
337                      * stored in the CellServDB file
338                      */
339                     code = sscanf(lineBuffer, "%d.%d.%d.%d #%s",
340                                    &c1, &c2, &c3, &c4, aname);
341                     tp = (char *) &ip_addr;
342                     *tp++ = c1;
343                     *tp++ = c2;
344                     *tp++ = c3;
345                     *tp++ = c4;
346                     memcpy(&vlSockAddr.sin_addr.s_addr, &ip_addr,
347                             sizeof(long));
348                     vlSockAddr.sin_family = AF_INET;
349                     /* sin_port supplied by connection code */
350                     if (procp)
351                         (*procp)(rockp, &vlSockAddr, valuep);
352                     foundCell = 1;
353                 }
354             }
355         }       /* a vldb line */
356     }           /* while loop processing all lines */
357
358     /* if for some unknown reason cell is not found, return negative code (-11) ??? */
359     return (foundCell) ? 0 : -11;
360 }
361
362 long cm_SearchCellByDNS(char *cellNamep, char *newCellNamep, int *ttl,
363                cm_configProc_t *procp, void *rockp)
364 {
365 #ifdef AFS_AFSDB_ENV
366     int rc;
367     int  cellHostAddrs[AFSMAXCELLHOSTS];
368     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
369     int numServers;
370     int i;
371     struct sockaddr_in vlSockAddr;
372
373 #ifdef DEBUG
374     DebugEvent_local("AFS SearchCellDNS-","Doing search for [%s]", cellNamep);
375 #endif
376     rc = getAFSServer(cellNamep, cellHostAddrs, cellHostNames, &numServers, ttl);
377     if (rc == 0 && numServers > 0) {     /* found the cell */
378         for (i = 0; i < numServers; i++) {
379             memcpy(&vlSockAddr.sin_addr.s_addr, &cellHostAddrs[i],
380                    sizeof(long));
381            vlSockAddr.sin_family = AF_INET;
382            /* sin_port supplied by connection code */
383            if (procp)
384           (*procp)(rockp, &vlSockAddr, cellHostNames[i]);
385            if(newCellNamep)
386           strcpy(newCellNamep,cellNamep);
387         }
388         return 0;   /* found cell */
389     }
390     else
391        return -1;  /* not found */
392 #else
393         return -1;  /* not found */
394 #endif /* AFS_AFSDB_ENV */
395 }
396
397 #if !defined(DJGPP) && !defined(AFS_WIN95_ENV)
398 /* look up the root cell's name in the Registry */
399 long cm_GetRootCellName(char *cellNamep)
400 {
401         DWORD code, dummyLen;
402         HKEY parmKey;
403
404         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName,
405                                 0, KEY_QUERY_VALUE, &parmKey);
406         if (code != ERROR_SUCCESS)
407                 return -1;
408
409         dummyLen = 256;
410         code = RegQueryValueEx(parmKey, "Cell", NULL, NULL,
411                                 cellNamep, &dummyLen);
412         RegCloseKey (parmKey);
413         if (code != ERROR_SUCCESS || cellNamep[0] == 0)
414                 return -1;
415
416         return 0;
417 }
418 #else
419 /* look up the root cell's name in the THISCELL file */
420 long cm_GetRootCellName(char *cellNamep)
421 {
422         FILE *thisCell;
423         char thisCellPath[256];
424         char *newline;
425
426 #ifdef DJGPP
427         strcpy(thisCellPath, cm_confDir);
428 #else
429         /* Win 95 */
430         char *afsconf_path;
431         afsconf_path = getenv("AFSCONF");
432         if (!afsconf_path)
433           strcpy(thisCellPath, AFSDIR_CLIENT_ETC_DIRPATH);
434         else
435           strcpy(thisCellPath, afsconf_path);
436 #endif
437         strcat(thisCellPath,"/");
438
439         strcat(thisCellPath, AFS_THISCELL);
440         thisCell = fopen(thisCellPath, "r");
441         if (thisCell == NULL)
442           return -1;
443
444         fgets(cellNamep, 256, thisCell);
445         fclose(thisCell);
446
447         newline = strrchr(cellNamep,'\n');
448         if (newline) *newline = '\0';
449         newline = strrchr(cellNamep,'\r');
450         if (newline) *newline = '\0';
451
452         return 0;
453 }
454 #endif /* !DJGPP */
455
456 cm_configFile_t *cm_CommonOpen(char *namep, char *rwp)
457 {
458         char wdir[256];
459     long code;
460     long tlen;
461     FILE *tfilep;
462
463 #if !defined(DJGPP) && !defined(AFS_WIN95_ENV)
464     strcpy(wdir, AFSDIR_CLIENT_ETC_DIRPATH);
465         
466         /* add trailing backslash, if required */
467         tlen = strlen(wdir);
468         if (wdir[tlen-1] != '\\') strcat(wdir, "\\");
469 #else
470 #ifdef DJGPP
471         strcpy(wdir,cm_confDir);
472 #else
473         char *afsconf_path = getenv("AFSCONF");
474         if (!afsconf_path)
475           strcpy(wdir, AFSDIR_CLIENT_ETC_DIRPATH);
476         else
477           strcpy(wdir, afsconf_path);
478 #endif /* !DJGPP */
479         strcat(wdir,"/");
480 #endif /* DJGPP || WIN95 */
481
482         strcat(wdir, namep);
483         
484         tfilep = fopen(wdir, rwp);
485
486         return ((cm_configFile_t *) tfilep);        
487 }
488
489 #ifndef DJGPP
490 long cm_WriteConfigString(char *labelp, char *valuep)
491 {
492         DWORD code, dummyDisp;
493         HKEY parmKey;
494
495         code = RegCreateKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName,
496                                 0, "container", 0, KEY_SET_VALUE, NULL,
497                                 &parmKey, &dummyDisp);
498         if (code != ERROR_SUCCESS)
499                 return -1;
500
501         code = RegSetValueEx(parmKey, labelp, 0, REG_SZ,
502                              valuep, strlen(valuep) + 1);
503         RegCloseKey (parmKey);
504         if (code != ERROR_SUCCESS)
505                 return -1;
506
507         return 0;
508 }
509 #endif /* !DJGPP */
510
511 #ifndef DJGPP
512 long cm_WriteConfigInt(char *labelp, long value)
513 {
514         DWORD code, dummyDisp;
515         HKEY parmKey;
516
517         code = RegCreateKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName,
518                                 0, "container", 0, KEY_SET_VALUE, NULL,
519                                 &parmKey, &dummyDisp);
520         if (code != ERROR_SUCCESS)
521                 return -1;
522
523         code = RegSetValueEx(parmKey, labelp, 0, REG_DWORD,
524                              (LPBYTE)&value, sizeof(value));
525         RegCloseKey (parmKey);
526         if (code != ERROR_SUCCESS)
527                 return -1;
528
529         return 0;
530 }
531 #endif /* !DJGPP */
532
533 cm_configFile_t *cm_OpenCellFile(void)
534 {
535         cm_configFile_t *cfp;
536
537         cfp = cm_CommonOpen("afsdcel2.ini", "w");
538         return cfp;
539 }
540
541 long cm_AppendPrunedCellList(cm_configFile_t *ofp, char *cellNamep)
542 {
543         cm_configFile_t *tfilep;        /* input file */
544         char *tp;
545         char lineBuffer[256];
546         char *valuep;
547         int inRightCell;
548         int foundCell;
549
550         tfilep = cm_CommonOpen(AFS_CELLSERVDB, "r");
551         if (!tfilep) return -1;
552
553         foundCell = 0;
554
555         /* have we seen the cell line for the guy we're looking for? */
556         inRightCell = 0;
557         while (1) {
558                 tp = fgets(lineBuffer, sizeof(lineBuffer), (FILE *)tfilep);
559                 if (tp == NULL) {
560                         if (feof((FILE *)tfilep)) {
561                                 /* hit EOF */
562                                 fclose((FILE *)tfilep);
563                                 return 0;
564                         }
565                 }
566                 
567                 /* turn trailing cr or lf into null */
568                 tp = strchr(lineBuffer, '\r');
569                 if (tp) *tp = 0;
570                 tp = strchr(lineBuffer, '\n');
571                 if (tp) *tp = 0;
572                 
573                 /* skip blank lines */
574                 if (lineBuffer[0] == 0) {
575                         fprintf((FILE *)ofp, "%s\n", lineBuffer);
576                         continue;
577                 }
578
579                 if (lineBuffer[0] == '>') {
580                         /* trim off at white space or '#' chars */
581                         tp = strchr(lineBuffer, ' ');
582                         if (tp) *tp = 0;
583                         tp = strchr(lineBuffer, '\t');
584                         if (tp) *tp = 0;
585                         tp = strchr(lineBuffer, '#');
586                         if (tp) *tp = 0;
587
588                         /* now see if this is the right cell */
589                         if (strcmp(lineBuffer+1, cellNamep) == 0) {
590                                 /* found the cell we're looking for */
591                                 inRightCell = 1;
592                         }
593                         else {
594                                 inRightCell = 0;
595                                 fprintf((FILE *)ofp, "%s\n", lineBuffer);
596                         }
597                 }
598                 else {
599                         valuep = strchr(lineBuffer, '#');
600                         if (valuep == NULL) return -2;
601                         valuep++;       /* skip the "#" */
602                         if (!inRightCell) {
603                                 fprintf((FILE *)ofp, "%s\n", lineBuffer);
604                         }
605                 }       /* a vldb line */
606         }               /* while loop processing all lines */
607 }
608
609 long cm_AppendNewCell(cm_configFile_t *filep, char *cellNamep)
610 {
611         fprintf((FILE *)filep, ">%s\n", cellNamep);
612         return 0;
613 }
614
615 long cm_AppendNewCellLine(cm_configFile_t *filep, char *linep)
616 {
617         fprintf((FILE *)filep, "%s\n", linep);
618         return 0;
619 }
620
621 long cm_CloseCellFile(cm_configFile_t *filep)
622 {
623         char wdir[256];
624     char sdir[256];
625     long code;
626     long closeCode;
627     int tlen;
628 #ifdef AFS_WIN95_ENV
629     char *afsconf_path;
630 #endif
631         closeCode = fclose((FILE *)filep);
632
633 #if !defined(DJGPP) && !defined(AFS_WIN95_ENV)
634     strcpy(wdir, AFSDIR_CLIENT_ETC_DIRPATH);
635         
636         /* add trailing backslash, if required */
637     tlen = strlen(wdir);
638     if (wdir[tlen-1] != '\\') strcat(wdir, "\\");
639 #else
640 #ifdef DJGPP
641     strcpy(wdir,cm_confDir);
642 #else
643     afsconf_path = getenv("AFSCONF");
644     if (!afsconf_path)
645         strcpy(wdir, AFSDIR_CLIENT_ETC_DIRPATH);
646     else
647         strcpy(wdir, afsconf_path);
648 #endif /* !DJGPP */
649     strcat(wdir,"/");
650 #endif /* DJGPP || WIN95 */
651
652     strcpy(sdir, wdir);
653
654         if (closeCode != 0) {
655                 /* something went wrong, preserve original database */
656         strcat(wdir, "afsdcel2.ini");
657         unlink(wdir);
658         return closeCode;
659     }
660
661     strcat(wdir, AFS_CELLSERVDB);
662     strcat(sdir, "afsdcel2.ini");       /* new file */
663
664     unlink(wdir);                       /* delete old file */
665
666     code = rename(sdir, wdir);  /* do the rename */
667
668     if (code) 
669         code = errno;
670
671     return code;
672 }   
673
674 void cm_GetConfigDir(char *dir)
675 {
676         char wdir[256];
677     int code;
678     int tlen;
679 #ifdef AFS_WIN95_ENV
680     char *afsconf_path;
681 #endif
682
683 #if !defined(DJGPP) && !defined(AFS_WIN95_ENV)
684     strcpy(wdir, AFSDIR_CLIENT_ETC_DIRPATH);
685         
686         /* add trailing backslash, if required */
687     tlen = strlen(wdir);
688     if (wdir[tlen-1] != '\\') strcat(wdir, "\\");
689 #else
690 #ifdef DJGPP
691     strcpy(wdir,cm_confDir);
692 #else
693     afsconf_path = getenv("AFSCONF");
694     if (!afsconf_path)
695         strcpy(wdir, AFSDIR_CLIENT_ETC_DIRPATH);
696     else
697         strcpy(wdir, afsconf_path);
698 #endif /* !DJGPP */
699     strcat(wdir,"\\");
700 #endif /* DJGPP || WIN95 */
701     strcpy(dir, wdir);
702 }