~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/* Copyright (C) 2000 MySQL AB
2
3
   This program is free software; you can redistribute it and/or modify
4
   it under the terms of the GNU General Public License as published by
5
   the Free Software Foundation; version 2 of the License.
6
7
   This program is distributed in the hope that it will be useful,
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
   GNU General Public License for more details.
11
12
   You should have received a copy of the GNU General Public License
13
   along with this program; if not, write to the Free Software
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
16
#include "mysys_priv.h"
17
#include <m_string.h>
53.2.7 by Monty Taylor
Changes so that removal of duplicate curr_dir from my_sys.h work.
18
#include "my_static.h"
1 by brian
clean slate
19
#ifdef HAVE_PWD_H
20
#include <pwd.h>
21
#endif
22
#ifdef VMS
23
#include <rms.h>
24
#include <iodef.h>
25
#include <descrip.h>
26
#endif /* VMS */
27
28
static char * expand_tilde(char * *path);
29
30
	/* Pack a dirname ; Changes HOME to ~/ and current dev to ./ */
31
	/* from is a dirname (from dirname() ?) ending with FN_LIBCHAR */
32
	/* to may be == from */
33
34
void pack_dirname(char * to, const char *from)
35
{
36
  int cwd_err;
37
  size_t d_length,length,buff_length= 0;
38
  char * start;
39
  char buff[FN_REFLEN];
40
  DBUG_ENTER("pack_dirname");
41
42
  (void) intern_filename(to,from);		/* Change to intern name */
43
44
#ifdef FN_DEVCHAR
45
  if ((start=strrchr(to,FN_DEVCHAR)) != 0)	/* Skip device part */
46
    start++;
47
  else
48
#endif
49
    start=to;
50
51
  if (!(cwd_err= my_getwd(buff,FN_REFLEN,MYF(0))))
52
  {
53
    buff_length= strlen(buff);
54
    d_length= (size_t) (start-to);
55
    if ((start == to ||
56
	 (buff_length == d_length && !bcmp(buff,start,d_length))) &&
57
	*start != FN_LIBCHAR && *start)
58
    {						/* Put current dir before */
59
      bchange((uchar*) to, d_length, (uchar*) buff, buff_length, strlen(to)+1);
60
    }
61
  }
62
63
  if ((d_length= cleanup_dirname(to,to)) != 0)
64
  {
65
    length=0;
66
    if (home_dir)
67
    {
68
      length= strlen(home_dir);
69
      if (home_dir[length-1] == FN_LIBCHAR)
70
	length--;				/* Don't test last '/' */
71
    }
72
    if (length > 1 && length < d_length)
73
    {						/* test if /xx/yy -> ~/yy */
74
      if (bcmp(to,home_dir,length) == 0 && to[length] == FN_LIBCHAR)
75
      {
76
	to[0]=FN_HOMELIB;			/* Filename begins with ~ */
77
	(void) strmov_overlapp(to+1,to+length);
78
      }
79
    }
80
    if (! cwd_err)
81
    {						/* Test if cwd is ~/... */
82
      if (length > 1 && length < buff_length)
83
      {
84
	if (bcmp(buff,home_dir,length) == 0 && buff[length] == FN_LIBCHAR)
85
	{
86
	  buff[0]=FN_HOMELIB;
87
	  (void) strmov_overlapp(buff+1,buff+length);
88
	}
89
      }
90
      if (is_prefix(to,buff))
91
      {
92
	length= strlen(buff);
93
	if (to[length])
94
	  (void) strmov_overlapp(to,to+length);	/* Remove everything before */
95
	else
96
	{
97
	  to[0]= FN_CURLIB;			/* Put ./ instead of cwd */
98
	  to[1]= FN_LIBCHAR;
99
	  to[2]= '\0';
100
	}
101
      }
102
    }
103
  }
104
  DBUG_PRINT("exit",("to: '%s'",to));
105
  DBUG_VOID_RETURN;
106
} /* pack_dirname */
107
108
109
/*
110
  remove unwanted chars from dirname
111
112
  SYNOPSIS
113
     cleanup_dirname()
114
     to		Store result here
115
     from	Dirname to fix.  May be same as to
116
117
  IMPLEMENTATION
118
  "/../" removes prev dir
119
  "/~/" removes all before ~
120
  //" is same as "/", except on Win32 at start of a file
121
  "/./" is removed
122
  Unpacks home_dir if "~/.." used
123
  Unpacks current dir if if "./.." used
124
125
  RETURN
126
    #  length of new name   
127
*/
128
129
size_t cleanup_dirname(register char *to, const char *from)
130
{
131
  register size_t length;
132
  register char * pos;
133
  register char * from_ptr;
134
  register char * start;
135
  char parent[5],				/* for "FN_PARENTDIR" */
136
       buff[FN_REFLEN+1],*end_parentdir;
137
#ifdef BACKSLASH_MBTAIL
138
  CHARSET_INFO *fs= fs_character_set();
139
#endif
140
  DBUG_ENTER("cleanup_dirname");
141
  DBUG_PRINT("enter",("from: '%s'",from));
142
143
  start=buff;
144
  from_ptr=(char *) from;
145
#ifdef FN_DEVCHAR
146
  if ((pos=strrchr(from_ptr,FN_DEVCHAR)) != 0)
147
  {						/* Skip device part */
148
    length=(size_t) (pos-from_ptr)+1;
149
    start=strnmov(buff,from_ptr,length); from_ptr+=length;
150
  }
151
#endif
152
153
  parent[0]=FN_LIBCHAR;
154
  length=(size_t) (strmov(parent+1,FN_PARENTDIR)-parent);
155
  for (pos=start ; (*pos= *from_ptr++) != 0 ; pos++)
156
  {
157
#ifdef BACKSLASH_MBTAIL
158
    uint l;
159
    if (use_mb(fs) && (l= my_ismbchar(fs, from_ptr - 1, from_ptr + 2)))
160
    {
161
      for (l-- ; l ; *++pos= *from_ptr++, l--);
162
      start= pos + 1; /* Don't look inside multi-byte char */
163
      continue;
164
    }
165
#endif
166
    if (*pos == '/')
167
      *pos = FN_LIBCHAR;
168
    if (*pos == FN_LIBCHAR)
169
    {
170
      if ((size_t) (pos-start) > length && bcmp(pos-length,parent,length) == 0)
171
      {						/* If .../../; skip prev */
172
	pos-=length;
173
	if (pos != start)
174
	{					 /* not /../ */
175
	  pos--;
176
	  if (*pos == FN_HOMELIB && (pos == start || pos[-1] == FN_LIBCHAR))
177
	  {
178
	    if (!home_dir)
179
	    {
180
	      pos+=length+1;			/* Don't unpack ~/.. */
181
	      continue;
182
	    }
183
	    pos=strmov(buff,home_dir)-1;	/* Unpacks ~/.. */
184
	    if (*pos == FN_LIBCHAR)
185
	      pos--;				/* home ended with '/' */
186
	  }
187
	  if (*pos == FN_CURLIB && (pos == start || pos[-1] == FN_LIBCHAR))
188
	  {
189
	    if (my_getwd(curr_dir,FN_REFLEN,MYF(0)))
190
	    {
191
	      pos+=length+1;			/* Don't unpack ./.. */
192
	      continue;
193
	    }
194
	    pos=strmov(buff,curr_dir)-1;	/* Unpacks ./.. */
195
	    if (*pos == FN_LIBCHAR)
196
	      pos--;				/* home ended with '/' */
197
	  }
198
	  end_parentdir=pos;
199
	  while (pos >= start && *pos != FN_LIBCHAR)	/* remove prev dir */
200
	    pos--;
201
	  if (pos[1] == FN_HOMELIB || bcmp(pos,parent,length) == 0)
202
	  {					/* Don't remove ~user/ */
203
	    pos=strmov(end_parentdir+1,parent);
204
	    *pos=FN_LIBCHAR;
205
	    continue;
206
	  }
207
	}
208
      }
209
      else if ((size_t) (pos-start) == length-1 &&
210
	       !bcmp(start,parent+1,length-1))
211
	start=pos;				/* Starts with "../" */
212
      else if (pos-start > 0 && pos[-1] == FN_LIBCHAR)
213
      {
214
#ifdef FN_NETWORK_DRIVES
215
	if (pos-start != 1)
216
#endif
217
	  pos--;			/* Remove dupplicate '/' */
218
      }
219
      else if (pos-start > 1 && pos[-1] == FN_CURLIB && pos[-2] == FN_LIBCHAR)
220
	pos-=2;					/* Skip /./ */
221
      else if (pos > buff+1 && pos[-1] == FN_HOMELIB && pos[-2] == FN_LIBCHAR)
222
      {					/* Found ..../~/  */
223
	buff[0]=FN_HOMELIB;
224
	buff[1]=FN_LIBCHAR;
225
	start=buff; pos=buff+1;
226
      }
227
    }
228
  }
229
  (void) strmov(to,buff);
230
  DBUG_PRINT("exit",("to: '%s'",to));
231
  DBUG_RETURN((size_t) (pos-buff));
232
} /* cleanup_dirname */
233
234
235
/*
236
  On system where you don't have symbolic links, the following
237
  code will allow you to create a file: 
238
  directory-name.sym that should contain the real path
239
  to the directory.  This will be used if the directory name
240
  doesn't exists
241
*/
242
243
244
my_bool my_use_symdir=0;	/* Set this if you want to use symdirs */
245
246
#ifdef USE_SYMDIR
247
void symdirget(char *dir)
248
{
249
  char buff[FN_REFLEN];
250
  char *pos=strend(dir);
251
  if (dir[0] && pos[-1] != FN_DEVCHAR && my_access(dir, F_OK))
252
  {
253
    File file;
254
    size_t length;
255
    char temp= *(--pos);            /* May be "/" or "\" */
256
    strmov(pos,".sym");
257
    file= my_open(dir, O_RDONLY, MYF(0));
258
    *pos++=temp; *pos=0;	  /* Restore old filename */
259
    if (file >= 0)
260
    {
261
      if ((length= my_read(file, buff, sizeof(buff), MYF(0))) > 0)
262
      {
263
	for (pos= buff + length ;
264
	     pos > buff && (iscntrl(pos[-1]) || isspace(pos[-1])) ;
265
	     pos --);
266
267
	/* Ensure that the symlink ends with the directory symbol */
268
	if (pos == buff || pos[-1] != FN_LIBCHAR)
269
	  *pos++=FN_LIBCHAR;
270
271
	strmake(dir,buff, (size_t) (pos-buff));
272
      }
273
      my_close(file, MYF(0));
274
    }
275
  }
276
}
277
#endif /* USE_SYMDIR */
278
279
280
/*
281
  Fixes a directroy name so that can be used by open()
282
283
  SYNOPSIS
284
    unpack_dirname()
285
    to			result-buffer, FN_REFLEN characters. may be == from
286
    from		'Packed' directory name (may contain ~)
287
288
 IMPLEMENTATION
289
  Make that last char of to is '/' if from not empty and
290
  from doesn't end in FN_DEVCHAR
291
  Uses cleanup_dirname and changes ~/.. to home_dir/..
292
293
  Changes a UNIX filename to system filename (replaces / with \ on windows)
294
295
  RETURN
296
   Length of new directory name (= length of to)
297
*/
298
299
size_t unpack_dirname(char * to, const char *from)
300
{
301
  size_t length, h_length;
302
  char buff[FN_REFLEN+1+4],*suffix,*tilde_expansion;
303
  DBUG_ENTER("unpack_dirname");
304
305
  (void) intern_filename(buff,from);	    /* Change to intern name */
306
  length= strlen(buff);                     /* Fix that '/' is last */
307
  if (length &&
308
#ifdef FN_DEVCHAR
309
      buff[length-1] != FN_DEVCHAR &&
310
#endif
311
      buff[length-1] != FN_LIBCHAR && buff[length-1] != '/')
312
  {
313
    buff[length]=FN_LIBCHAR;
314
    buff[length+1]= '\0';
315
  }
316
317
  length=cleanup_dirname(buff,buff);
318
  if (buff[0] == FN_HOMELIB)
319
  {
320
    suffix=buff+1; tilde_expansion=expand_tilde(&suffix);
321
    if (tilde_expansion)
322
    {
323
      length-= (size_t) (suffix-buff)-1;
324
      if (length+(h_length= strlen(tilde_expansion)) <= FN_REFLEN)
325
      {
326
	if (tilde_expansion[h_length-1] == FN_LIBCHAR)
327
	  h_length--;
328
	if (buff+h_length < suffix)
329
	  bmove(buff+h_length,suffix,length);
330
	else
331
	  bmove_upp((uchar*) buff+h_length+length, (uchar*) suffix+length, length);
332
	bmove(buff,tilde_expansion,h_length);
333
      }
334
    }
335
  }
336
#ifdef USE_SYMDIR
337
  if (my_use_symdir)
338
    symdirget(buff);
339
#endif
340
  DBUG_RETURN(system_filename(to,buff));	/* Fix for open */
341
} /* unpack_dirname */
342
343
344
	/* Expand tilde to home or user-directory */
345
	/* Path is reset to point at FN_LIBCHAR after ~xxx */
346
347
static char * expand_tilde(char * *path)
348
{
349
  if (path[0][0] == FN_LIBCHAR)
350
    return home_dir;			/* ~/ expanded to home */
351
#ifdef HAVE_GETPWNAM
352
  {
353
    char *str,save;
354
    struct passwd *user_entry;
355
356
    if (!(str=strchr(*path,FN_LIBCHAR)))
357
      str=strend(*path);
358
    save= *str; *str= '\0';
359
    user_entry=getpwnam(*path);
360
    *str=save;
361
    endpwent();
362
    if (user_entry)
363
    {
364
      *path=str;
365
      return user_entry->pw_dir;
366
    }
367
  }
368
#endif
369
  return (char *) 0;
370
}
371
372
373
/*
374
  Fix filename so it can be used by open, create
375
376
  SYNOPSIS
377
    unpack_filename()
378
    to		Store result here. Must be at least of size FN_REFLEN.
379
    from	Filename in unix format (with ~)
380
381
  RETURN
382
    # length of to
383
384
  NOTES
385
    to may be == from
386
    ~ will only be expanded if total length < FN_REFLEN
387
*/
388
389
390
size_t unpack_filename(char * to, const char *from)
391
{
392
  size_t length, n_length, buff_length;
393
  char buff[FN_REFLEN];
394
  DBUG_ENTER("unpack_filename");
395
396
  length=dirname_part(buff, from, &buff_length);/* copy & convert dirname */
397
  n_length=unpack_dirname(buff,buff);
398
  if (n_length+strlen(from+length) < FN_REFLEN)
399
  {
400
    (void) strmov(buff+n_length,from+length);
401
    length= system_filename(to,buff);		/* Fix to usably filename */
402
  }
403
  else
404
    length= system_filename(to,from);		/* Fix to usably filename */
405
  DBUG_RETURN(length);
406
} /* unpack_filename */
407
408
409
	/* Convert filename (unix standard) to system standard */
410
	/* Used before system command's like open(), create() .. */
411
	/* Returns used length of to; total length should be FN_REFLEN */
412
413
size_t system_filename(char * to, const char *from)
414
{
415
#ifndef FN_C_BEFORE_DIR
416
  return (size_t) (strmake(to,from,FN_REFLEN-1)-to);
417
#else	/* VMS */
418
419
	/* change 'dev:lib/xxx' to 'dev:[lib]xxx' */
420
	/* change 'dev:xxx' to 'dev:xxx' */
421
	/* change './xxx' to 'xxx' */
422
	/* change './lib/' or lib/ to '[.lib]' */
423
	/* change '/x/y/z to '[x.y]x' */
424
	/* change 'dev:/x' to 'dev:[000000]x' */
425
426
  int libchar_found;
427
  size_t length;
428
  char * to_pos,from_pos,pos;
429
  char buff[FN_REFLEN];
430
  DBUG_ENTER("system_filename");
431
432
  libchar_found=0;
433
  (void) strmov(buff,from);			 /* If to == from */
434
  from_pos= buff;
435
  if ((pos=strrchr(from_pos,FN_DEVCHAR)))	/* Skip device part */
436
  {
437
    pos++;
438
    to_pos=strnmov(to,from_pos,(size_t) (pos-from_pos));
439
    from_pos=pos;
440
  }
441
  else
442
    to_pos=to;
443
444
  if (from_pos[0] == FN_CURLIB && from_pos[1] == FN_LIBCHAR)
445
    from_pos+=2;				/* Skip './' */
446
  if (strchr(from_pos,FN_LIBCHAR))
447
  {
448
    *(to_pos++) = FN_C_BEFORE_DIR;
449
    if (strinstr(from_pos,FN_ROOTDIR) == 1)
450
    {
451
      from_pos+=strlen(FN_ROOTDIR);		/* Actually +1 but... */
452
      if (! strchr(from_pos,FN_LIBCHAR))
453
      {						/* No dir, use [000000] */
454
	to_pos=strmov(to_pos,FN_C_ROOT_DIR);
455
	libchar_found++;
456
      }
457
    }
458
    else
459
      *(to_pos++)=FN_C_DIR_SEP;			/* '.' gives current dir */
460
461
    while ((pos=strchr(from_pos,FN_LIBCHAR)))
462
    {
463
      if (libchar_found++)
464
	*(to_pos++)=FN_C_DIR_SEP;		/* Add '.' between dirs */
465
      if (strinstr(from_pos,FN_PARENTDIR) == 1 &&
466
	  from_pos+strlen(FN_PARENTDIR) == pos)
467
	to_pos=strmov(to_pos,FN_C_PARENT_DIR);	/* Found '../' */
468
      else
469
	to_pos=strnmov(to_pos,from_pos,(size_t) (pos-from_pos));
470
      from_pos=pos+1;
471
    }
472
    *(to_pos++)=FN_C_AFTER_DIR;
473
  }
474
  length= (size_t) (strmov(to_pos,from_pos)-to);
475
  DBUG_PRINT("exit",("name: '%s'",to));
476
  DBUG_RETURN(length);
477
#endif
478
} /* system_filename */
479
480
481
	/* Fix a filename to intern (UNIX format) */
482
483
char *intern_filename(char *to, const char *from)
484
{
485
  size_t length, to_length;
486
  char buff[FN_REFLEN];
487
  if (from == to)
488
  {						/* Dirname may destroy from */
489
    strmov(buff,from);
490
    from=buff;
491
  }
492
  length= dirname_part(to, from, &to_length);	/* Copy dirname & fix chars */
493
  (void) strmov(to + to_length,from+length);
494
  return (to);
495
} /* intern_filename */