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