root/src/vfs/smbfs/helpers/lib/util.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. tmpdir
  2. in_group
  3. Atoic
  4. get_numlist
  5. putip
  6. dns_to_netbios_name
  7. name_interpret
  8. name_mangle
  9. file_exist
  10. file_modtime
  11. directory_exist
  12. file_size
  13. attrib_string
  14. unix_format
  15. dos_format
  16. show_msg
  17. smb_len
  18. _smb_setlen
  19. smb_setlen
  20. set_message
  21. smb_numwords
  22. smb_buflen
  23. smb_buf_ofs
  24. smb_buf
  25. smb_offset
  26. dos_clean_name
  27. unix_clean_name
  28. reduce_name
  29. expand_one
  30. dirname_dos
  31. filename_dos
  32. expand_mask
  33. make_dir_struct
  34. close_low_fds
  35. set_blocking
  36. TvalDiff
  37. transfer_file
  38. name_ptr
  39. name_extract
  40. name_len
  41. msleep
  42. unix_do_match
  43. unix_mask_match
  44. do_match
  45. mask_match
  46. set_filelen
  47. setbuffer
  48. Realloc
  49. get_myname
  50. ip_equal
  51. interpret_protocol
  52. interpret_addr
  53. interpret_addr2
  54. zero_ip
  55. matchname
  56. strip_mount_options
  57. automount_lookup
  58. automount_lookup
  59. automount_server
  60. automount_path
  61. standard_sub_basic
  62. standard_sub
  63. same_net
  64. Get_Hostbyname
  65. uidtoname
  66. gidtoname
  67. nametouid
  68. smb_panic
  69. readdirname
  70. is_in_path
  71. set_namearray
  72. free_namearray
  73. is_myname
  74. set_remote_arch
  75. get_remote_arch
  76. align2
  77. out_ascii
  78. out_data
  79. print_asc
  80. dump_data
  81. str_checksum
  82. zero_free

   1 /*
   2    Unix SMB/Netbios implementation.
   3    Version 1.9.
   4    Samba utility functions
   5 
   6    Copyright (C) Andrew Tridgell 1992-1998
   7 
   8    Copyright (C) 2011-2019
   9    Free Software Foundation, Inc.
  10 
  11    This file is part of the Midnight Commander.
  12 
  13    The Midnight Commander is free software: you can redistribute it
  14    and/or modify it under the terms of the GNU General Public License as
  15    published by the Free Software Foundation, either version 3 of the License,
  16    or (at your option) any later version.
  17 
  18    The Midnight Commander is distributed in the hope that it will be useful,
  19    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21    GNU General Public License for more details.
  22 
  23    You should have received a copy of the GNU General Public License
  24    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  25  */
  26 
  27 #include "includes.h"
  28 
  29 #if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT))
  30 #ifdef WITH_NISPLUS_HOME
  31 #ifdef BROKEN_NISPLUS_INCLUDE_FILES
  32 /*
  33  * The following lines are needed due to buggy include files
  34  * in Solaris 2.6 which define GROUP in both /usr/include/sys/acl.h and
  35  * also in /usr/include/rpcsvc/nis.h. The definitions conflict. JRA.
  36  * Also GROUP_OBJ is defined as 0x4 in /usr/include/sys/acl.h and as
  37  * an enum in /usr/include/rpcsvc/nis.h.
  38  */
  39 
  40 #if defined(GROUP)
  41 #undef GROUP
  42 #endif
  43 
  44 #if defined(GROUP_OBJ)
  45 #undef GROUP_OBJ
  46 #endif
  47 
  48 #endif /* BROKEN_NISPLUS_INCLUDE_FILES */
  49 
  50 #include <rpcsvc/nis.h>
  51 
  52 #else /* !WITH_NISPLUS_HOME */
  53 
  54 #include "rpcsvc/ypclnt.h"
  55 
  56 #endif /* WITH_NISPLUS_HOME */
  57 #endif /* HAVE_NETGROUP && WITH_AUTOMOUNT */
  58 
  59 #ifdef WITH_SSL
  60 #include <ssl.h>
  61 #undef Realloc                  /* SSLeay defines this and samba has a function of this name */
  62 #endif /* WITH_SSL */
  63 
  64 extern int DEBUGLEVEL;
  65 #if 0
  66 int Protocol = PROTOCOL_COREPLUS;
  67 #endif /*0 */
  68 
  69 /* a default finfo structure to ensure all fields are sensible */
  70 file_info const def_finfo = { -1, 0, 0, 0, 0, 0, 0, "" };
  71 
  72 /* the client file descriptor */
  73 extern int Client;
  74 
  75 /* this is used by the chaining code */
  76 const int chain_size = 0;
  77 
  78 /*
  79    case handling on filenames 
  80  */
  81 const int case_default = CASE_LOWER;
  82 
  83 #if 0
  84 /* the following control case operations - they are put here so the
  85    client can link easily */
  86 BOOL case_sensitive;
  87 BOOL case_preserve;
  88 BOOL use_mangled_map = False;
  89 BOOL short_case_preserve;
  90 BOOL case_mangle;
  91 #endif /*0 */
  92 
  93 static const char *remote_machine = "";
  94 static const char *local_machine = "";
  95 static const char *remote_arch = "UNKNOWN";
  96 #if 0
  97 static enum remote_arch_types ra_type = RA_UNKNOWN;
  98 #endif
  99 static const char *remote_proto = "UNKNOWN";
 100 pstring myhostname = "";
 101 pstring user_socket_options = "";
 102 
 103 static const char sesssetup_user[] = "";
 104 static const char *const samlogon_user = "";
 105 
 106 const BOOL sam_logon_in_ssb = False;
 107 
 108 pstring global_myname = "";
 109 #if 0
 110 char **my_netbios_names;
 111 #endif /*0 */
 112 
 113 
 114 /****************************************************************************
 115   find a suitable temporary directory. The result should be copied immediately
 116   as it may be overwritten by a subsequent call
 117   ****************************************************************************/
 118 const char *
 119 tmpdir (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 120 {
 121     char *p;
 122     if ((p = getenv ("MC_TMPDIR")) || (p = getenv ("TMPDIR")))
 123     {
 124         return p;
 125     }
 126     return "/tmp";
 127 }
 128 
 129 /****************************************************************************
 130 determine whether we are in the specified group
 131 ****************************************************************************/
 132 #if 0
 133 BOOL
 134 in_group (gid_t group, gid_t current_gid, int ngroups, gid_t * groups)
     /* [previous][next][first][last][top][bottom][index][help]  */
 135 {
 136     int i;
 137 
 138     if (group == current_gid)
 139         return (True);
 140 
 141     for (i = 0; i < ngroups; i++)
 142         if (group == groups[i])
 143             return (True);
 144 
 145     return (False);
 146 }
 147 
 148 
 149 /****************************************************************************
 150 like atoi but gets the value up to the separater character
 151 ****************************************************************************/
 152 char *
 153 Atoic (char *p, int *n, char *c)
     /* [previous][next][first][last][top][bottom][index][help]  */
 154 {
 155     if (!isdigit ((int) *p))
 156     {
 157         DEBUG (5, ("Atoic: malformed number\n"));
 158         return NULL;
 159     }
 160 
 161     (*n) = atoi (p);
 162 
 163     while ((*p) && isdigit ((int) *p))
 164     {
 165         p++;
 166     }
 167 
 168     if (strchr (c, *p) == NULL)
 169     {
 170         DEBUG (5, ("Atoic: no separator characters (%s) not found\n", c));
 171         return NULL;
 172     }
 173 
 174     return p;
 175 }
 176 
 177 /*************************************************************************
 178  reads a list of numbers
 179  *************************************************************************/
 180 char *
 181 get_numlist (char *p, uint32 ** num, int *count)
     /* [previous][next][first][last][top][bottom][index][help]  */
 182 {
 183     int val;
 184 
 185     if (num == NULL || count == NULL)
 186     {
 187         return NULL;
 188     }
 189 
 190     (*count) = 0;
 191     (*num) = NULL;
 192 
 193     while ((p = Atoic (p, &val, ":,")) != NULL && (*p) != ':')
 194     {
 195         (*num) = Realloc ((*num), ((*count) + 1) * sizeof (uint32));
 196         if ((*num) == NULL)
 197         {
 198             return NULL;
 199         }
 200         (*num)[(*count)] = val;
 201         (*count)++;
 202         p++;
 203     }
 204 
 205     return p;
 206 }
 207 #endif /* 0 */
 208 /*******************************************************************
 209 copy an IP address from one buffer to another
 210 ********************************************************************/
 211 void
 212 putip (void *dest, void *src)
     /* [previous][next][first][last][top][bottom][index][help]  */
 213 {
 214     memcpy (dest, src, 4);
 215 }
 216 
 217 
 218 #define TRUNCATE_NETBIOS_NAME 1
 219 #if 0
 220 /*******************************************************************
 221  convert, possibly using a stupid microsoft-ism which has destroyed
 222  the transport independence of netbios (for CIFS vendors that usually
 223  use the Win95-type methods, not for NT to NT communication, which uses
 224  DCE/RPC and therefore full-length unicode strings...) a dns name into
 225  a netbios name.
 226 
 227  the netbios name (NOT necessarily null-terminated) is truncated to 15
 228  characters.
 229 
 230  ******************************************************************/
 231 char *
 232 dns_to_netbios_name (char *dns_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 233 {
 234     static char netbios_name[16];
 235     int i;
 236     StrnCpy (netbios_name, dns_name, 15);
 237     netbios_name[15] = 0;
 238 
 239 #ifdef TRUNCATE_NETBIOS_NAME
 240     /* ok.  this is because of a stupid microsoft-ism.  if the called host
 241        name contains a '.', microsoft clients expect you to truncate the
 242        netbios name up to and including the '.'  this even applies, by
 243        mistake, to workgroup (domain) names, which is _really_ daft.
 244      */
 245     for (i = 15; i >= 0; i--)
 246     {
 247         if (netbios_name[i] == '.')
 248         {
 249             netbios_name[i] = 0;
 250             break;
 251         }
 252     }
 253 #endif /* TRUNCATE_NETBIOS_NAME */
 254 
 255     return netbios_name;
 256 }
 257 
 258 
 259 /****************************************************************************
 260 interpret the weird netbios "name". Return the name type
 261 ****************************************************************************/
 262 static int
 263 name_interpret (char *in, char *out)
     /* [previous][next][first][last][top][bottom][index][help]  */
 264 {
 265     int ret;
 266     int len = (*in++) / 2;
 267 
 268     *out = 0;
 269 
 270     if (len > 30 || len < 1)
 271         return (0);
 272 
 273     while (len--)
 274     {
 275         if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P')
 276         {
 277             *out = 0;
 278             return (0);
 279         }
 280         *out = ((in[0] - 'A') << 4) + (in[1] - 'A');
 281         in += 2;
 282         out++;
 283     }
 284     *out = 0;
 285     ret = out[-1];
 286 
 287 #ifdef NETBIOS_SCOPE
 288     /* Handle any scope names */
 289     while (*in)
 290     {
 291         *out++ = '.';           /* Scope names are separated by periods */
 292         len = *(unsigned char *) in++;
 293         StrnCpy (out, in, len);
 294         out += len;
 295         *out = 0;
 296         in += len;
 297     }
 298 #endif
 299     return (ret);
 300 }
 301 #endif /* 0 */
 302 
 303 /****************************************************************************
 304 mangle a name into netbios format
 305 
 306   Note:  <Out> must be (33 + strlen(scope) + 2) bytes long, at minimum.
 307 ****************************************************************************/
 308 int
 309 name_mangle (char *In, char *Out, char name_type)
     /* [previous][next][first][last][top][bottom][index][help]  */
 310 {
 311     int i;
 312     int c;
 313     int len;
 314     char buf[20];
 315     char *p = Out;
 316     extern pstring global_scope;
 317 
 318     /* Safely copy the input string, In, into buf[]. */
 319     (void) memset (buf, 0, 20);
 320     if (strcmp (In, "*") == 0)
 321         buf[0] = '*';
 322     else
 323         (void) slprintf (buf, sizeof (buf) - 1, "%-15.15s%c", In, name_type);
 324 
 325     /* Place the length of the first field into the output buffer. */
 326     p[0] = 32;
 327     p++;
 328 
 329     /* Now convert the name to the rfc1001/1002 format. */
 330     for (i = 0; i < 16; i++)
 331     {
 332         c = toupper (buf[i]);
 333         p[i * 2] = ((c >> 4) & 0x000F) + 'A';
 334         p[(i * 2) + 1] = (c & 0x000F) + 'A';
 335     }
 336     p += 32;
 337     p[0] = '\0';
 338 
 339     /* Add the scope string. */
 340     for (i = 0, len = 0;; i++, len++)
 341     {
 342         switch (global_scope[i])
 343         {
 344         case '\0':
 345             p[0] = len;
 346             if (len > 0)
 347                 p[len + 1] = 0;
 348             return (name_len (Out));
 349         case '.':
 350             p[0] = len;
 351             p += (len + 1);
 352             len = -1;
 353             break;
 354         default:
 355             p[len + 1] = global_scope[i];
 356             break;
 357         }
 358     }
 359 
 360     return (name_len (Out));
 361 }                               /* name_mangle */
 362 
 363 /*******************************************************************
 364   check if a file exists
 365 ********************************************************************/
 366 BOOL
 367 file_exist (char *fname, SMB_STRUCT_STAT * sbuf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 368 {
 369     SMB_STRUCT_STAT st;
 370     if (!sbuf)
 371         sbuf = &st;
 372 
 373     if (sys_stat (fname, sbuf) != 0)
 374         return (False);
 375 
 376     return (S_ISREG (sbuf->st_mode));
 377 }
 378 
 379 /*******************************************************************
 380 check a files mod time
 381 ********************************************************************/
 382 time_t
 383 file_modtime (char *fname)
     /* [previous][next][first][last][top][bottom][index][help]  */
 384 {
 385     SMB_STRUCT_STAT st;
 386 
 387     if (sys_stat (fname, &st) != 0)
 388         return (0);
 389 
 390     return (st.st_mtime);
 391 }
 392 
 393 #if 0
 394 /*******************************************************************
 395   check if a directory exists
 396 ********************************************************************/
 397 BOOL
 398 directory_exist (char *dname, SMB_STRUCT_STAT * st)
     /* [previous][next][first][last][top][bottom][index][help]  */
 399 {
 400     SMB_STRUCT_STAT st2;
 401     BOOL ret;
 402 
 403     if (!st)
 404         st = &st2;
 405 
 406     if (sys_stat (dname, st) != 0)
 407         return (False);
 408 
 409     ret = S_ISDIR (st->st_mode);
 410     if (!ret)
 411         errno = ENOTDIR;
 412     return ret;
 413 }
 414 
 415 /*******************************************************************
 416 returns the size in bytes of the named file
 417 ********************************************************************/
 418 SMB_OFF_T
 419 file_size (char *file_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 420 {
 421     SMB_STRUCT_STAT buf;
 422     buf.st_size = 0;
 423     if (sys_stat (file_name, &buf) != 0)
 424         return (SMB_OFF_T) - 1;
 425     return (buf.st_size);
 426 }
 427 #endif /* 0 */
 428 
 429 /*******************************************************************
 430 return a string representing an attribute for a file
 431 ********************************************************************/
 432 char *
 433 attrib_string (uint16 mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
 434 {
 435     static char attrstr[7];
 436     int i = 0;
 437 
 438     attrstr[0] = 0;
 439 
 440     if (mode & aVOLID)
 441         attrstr[i++] = 'V';
 442     if (mode & aDIR)
 443         attrstr[i++] = 'D';
 444     if (mode & aARCH)
 445         attrstr[i++] = 'A';
 446     if (mode & aHIDDEN)
 447         attrstr[i++] = 'H';
 448     if (mode & aSYSTEM)
 449         attrstr[i++] = 'S';
 450     if (mode & aRONLY)
 451         attrstr[i++] = 'R';
 452 
 453     attrstr[i] = 0;
 454 
 455     return (attrstr);
 456 }
 457 
 458 #if 0
 459 /****************************************************************************
 460   make a file into unix format
 461 ****************************************************************************/
 462 void
 463 unix_format (char *fname)
     /* [previous][next][first][last][top][bottom][index][help]  */
 464 {
 465     string_replace (fname, '\\', '/');
 466 }
 467 
 468 /****************************************************************************
 469   make a file into dos format
 470 ****************************************************************************/
 471 void
 472 dos_format (char *fname)
     /* [previous][next][first][last][top][bottom][index][help]  */
 473 {
 474     string_replace (fname, '/', '\\');
 475 }
 476 #endif /* 0 */
 477 /*******************************************************************
 478   show a smb message structure
 479 ********************************************************************/
 480 void
 481 show_msg (char *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 482 {
 483     int i;
 484     int bcc = 0;
 485 
 486     if (DEBUGLEVEL < 5)
 487         return;
 488 
 489     DEBUG (5,
 490            ("size=%d\nsmb_com=0x%x\nsmb_rcls=%d\nsmb_reh=%d\nsmb_err=%d\nsmb_flg=%d\nsmb_flg2=%d\n",
 491             smb_len (buf), (int) CVAL (buf, smb_com), (int) CVAL (buf, smb_rcls), (int) CVAL (buf,
 492                                                                                               smb_reh),
 493             (int) SVAL (buf, smb_err), (int) CVAL (buf, smb_flg), (int) SVAL (buf, smb_flg2)));
 494     DEBUG (5,
 495            ("smb_tid=%d\nsmb_pid=%d\nsmb_uid=%d\nsmb_mid=%d\nsmt_wct=%d\n",
 496             (int) SVAL (buf, smb_tid), (int) SVAL (buf, smb_pid), (int) SVAL (buf, smb_uid),
 497             (int) SVAL (buf, smb_mid), (int) CVAL (buf, smb_wct)));
 498 
 499     for (i = 0; i < (int) CVAL (buf, smb_wct); i++)
 500     {
 501         DEBUG (5, ("smb_vwv[%d]=%d (0x%X)\n", i,
 502                    SVAL (buf, smb_vwv + 2 * i), SVAL (buf, smb_vwv + 2 * i)));
 503     }
 504 
 505     bcc = (int) SVAL (buf, smb_vwv + 2 * (CVAL (buf, smb_wct)));
 506 
 507     DEBUG (5, ("smb_bcc=%d\n", bcc));
 508 
 509     if (DEBUGLEVEL < 10)
 510         return;
 511 
 512     if (DEBUGLEVEL < 50)
 513     {
 514         bcc = MIN (bcc, 512);
 515     }
 516 
 517     dump_data (10, smb_buf (buf), bcc);
 518 }
 519 
 520 /*******************************************************************
 521   return the length of an smb packet
 522 ********************************************************************/
 523 int
 524 smb_len (char *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 525 {
 526     return (PVAL (buf, 3) | (PVAL (buf, 2) << 8) | ((PVAL (buf, 1) & 1) << 16));
 527 }
 528 
 529 /*******************************************************************
 530   set the length of an smb packet
 531 ********************************************************************/
 532 void
 533 _smb_setlen (char *buf, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 534 {
 535     buf[0] = 0;
 536     buf[1] = (len & 0x10000) >> 16;
 537     buf[2] = (len & 0xFF00) >> 8;
 538     buf[3] = len & 0xFF;
 539 }
 540 
 541 /*******************************************************************
 542   set the length and marker of an smb packet
 543 ********************************************************************/
 544 void
 545 smb_setlen (char *buf, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 546 {
 547     _smb_setlen (buf, len);
 548 
 549     CVAL (buf, 4) = 0xFF;
 550     CVAL (buf, 5) = 'S';
 551     CVAL (buf, 6) = 'M';
 552     CVAL (buf, 7) = 'B';
 553 }
 554 
 555 /*******************************************************************
 556   setup the word count and byte count for a smb message
 557 ********************************************************************/
 558 int
 559 set_message (char *buf, int num_words, int num_bytes, BOOL zero)
     /* [previous][next][first][last][top][bottom][index][help]  */
 560 {
 561     if (zero)
 562         memset (buf + smb_size, '\0', num_words * 2 + num_bytes);
 563     CVAL (buf, smb_wct) = num_words;
 564     SSVAL (buf, smb_vwv + num_words * SIZEOFWORD, num_bytes);
 565     smb_setlen (buf, smb_size + num_words * 2 + num_bytes - 4);
 566     return (smb_size + num_words * 2 + num_bytes);
 567 }
 568 
 569 /*******************************************************************
 570 return the number of smb words
 571 ********************************************************************/
 572 static int
 573 smb_numwords (char *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 574 {
 575     return (CVAL (buf, smb_wct));
 576 }
 577 
 578 /*******************************************************************
 579 return the size of the smb_buf region of a message
 580 ********************************************************************/
 581 int
 582 smb_buflen (char *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 583 {
 584     return (SVAL (buf, smb_vwv0 + smb_numwords (buf) * 2));
 585 }
 586 
 587 /*******************************************************************
 588   return a pointer to the smb_buf data area
 589 ********************************************************************/
 590 static int
 591 smb_buf_ofs (char *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 592 {
 593     return (smb_size + CVAL (buf, smb_wct) * 2);
 594 }
 595 
 596 /*******************************************************************
 597   return a pointer to the smb_buf data area
 598 ********************************************************************/
 599 char *
 600 smb_buf (char *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 601 {
 602     return (buf + smb_buf_ofs (buf));
 603 }
 604 
 605 /*******************************************************************
 606 return the SMB offset into an SMB buffer
 607 ********************************************************************/
 608 int
 609 smb_offset (char *p, char *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 610 {
 611     return (PTR_DIFF (p, buf + 4) + chain_size);
 612 }
 613 
 614 #if 0
 615 /*******************************************************************
 616 reduce a file name, removing .. elements.
 617 ********************************************************************/
 618 void
 619 dos_clean_name (char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 620 {
 621     char *p = NULL;
 622 
 623     DEBUG (3, ("dos_clean_name [%s]\n", s));
 624 
 625     /* remove any double slashes */
 626     string_sub (s, "\\\\", "\\");
 627 
 628     while ((p = strstr (s, "\\..\\")) != NULL)
 629     {
 630         pstring s1;
 631 
 632         *p = 0;
 633         pstrcpy (s1, p + 3);
 634 
 635         if ((p = strrchr (s, '\\')) != NULL)
 636             *p = 0;
 637         else
 638             *s = 0;
 639         pstrcat (s, s1);
 640     }
 641 
 642     trim_string (s, NULL, "\\..");
 643 
 644     string_sub (s, "\\.\\", "\\");
 645 }
 646 
 647 /*******************************************************************
 648 reduce a file name, removing .. elements. 
 649 ********************************************************************/
 650 void
 651 unix_clean_name (char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 652 {
 653     char *p = NULL;
 654 
 655     DEBUG (3, ("unix_clean_name [%s]\n", s));
 656 
 657     /* remove any double slashes */
 658     string_sub (s, "//", "/");
 659 
 660     /* Remove leading ./ characters */
 661     if (strncmp (s, "./", 2) == 0)
 662     {
 663         trim_string (s, "./", NULL);
 664         if (*s == 0)
 665             pstrcpy (s, "./");
 666     }
 667 
 668     while ((p = strstr (s, "/../")) != NULL)
 669     {
 670         pstring s1;
 671 
 672         *p = 0;
 673         pstrcpy (s1, p + 3);
 674 
 675         if ((p = strrchr (s, '/')) != NULL)
 676             *p = 0;
 677         else
 678             *s = 0;
 679         pstrcat (s, s1);
 680     }
 681 
 682     trim_string (s, NULL, "/..");
 683 }
 684 
 685 /*******************************************************************
 686 reduce a file name, removing .. elements and checking that 
 687 it is below dir in the heirachy. This uses dos_GetWd() and so must be run
 688 on the system that has the referenced file system.
 689 
 690 widelinks are allowed if widelinks is true
 691 ********************************************************************/
 692 BOOL
 693 reduce_name (char *s, char *dir, BOOL widelinks)
     /* [previous][next][first][last][top][bottom][index][help]  */
 694 {
 695 #ifndef REDUCE_PATHS
 696     return True;
 697 #else
 698     pstring dir2;
 699     pstring wd;
 700     pstring base_name;
 701     pstring newname;
 702     char *p = NULL;
 703     BOOL relative = (*s != '/');
 704 
 705     *dir2 = *wd = *base_name = *newname = 0;
 706 
 707     if (widelinks)
 708     {
 709         unix_clean_name (s);
 710         /* can't have a leading .. */
 711         if (strncmp (s, "..", 2) == 0 && (s[2] == 0 || s[2] == '/'))
 712         {
 713             DEBUG (3, ("Illegal file name? (%s)\n", s));
 714             return (False);
 715         }
 716 
 717         if (strlen (s) == 0)
 718             pstrcpy (s, "./");
 719 
 720         return (True);
 721     }
 722 
 723     DEBUG (3, ("reduce_name [%s] [%s]\n", s, dir));
 724 
 725     /* remove any double slashes */
 726     string_sub (s, "//", "/");
 727 
 728     pstrcpy (base_name, s);
 729     p = strrchr (base_name, '/');
 730 
 731     if (!p)
 732         return (True);
 733 
 734     if (!dos_GetWd (wd))
 735     {
 736         DEBUG (0, ("couldn't getwd for %s %s\n", s, dir));
 737         return (False);
 738     }
 739 
 740     if (dos_ChDir (dir) != 0)
 741     {
 742         DEBUG (0, ("couldn't chdir to %s\n", dir));
 743         return (False);
 744     }
 745 
 746     if (!dos_GetWd (dir2))
 747     {
 748         DEBUG (0, ("couldn't getwd for %s\n", dir));
 749         dos_ChDir (wd);
 750         return (False);
 751     }
 752 
 753     if (p && (p != base_name))
 754     {
 755         *p = 0;
 756         if (strcmp (p + 1, ".") == 0)
 757             p[1] = 0;
 758         if (strcmp (p + 1, "..") == 0)
 759             *p = '/';
 760     }
 761 
 762     if (dos_ChDir (base_name) != 0)
 763     {
 764         dos_ChDir (wd);
 765         DEBUG (3, ("couldn't chdir for %s %s basename=%s\n", s, dir, base_name));
 766         return (False);
 767     }
 768 
 769     if (!dos_GetWd (newname))
 770     {
 771         dos_ChDir (wd);
 772         DEBUG (2, ("couldn't get wd for %s %s\n", s, dir2));
 773         return (False);
 774     }
 775 
 776     if (p && (p != base_name))
 777     {
 778         pstrcat (newname, "/");
 779         pstrcat (newname, p + 1);
 780     }
 781 
 782     {
 783         size_t l = strlen (dir2);
 784         if (dir2[l - 1] == '/')
 785             l--;
 786 
 787         if (strncmp (newname, dir2, l) != 0)
 788         {
 789             dos_ChDir (wd);
 790             DEBUG (2,
 791                    ("Bad access attempt? s=%s dir=%s newname=%s l=%d\n", s, dir2, newname,
 792                     (int) l));
 793             return (False);
 794         }
 795 
 796         if (relative)
 797         {
 798             if (newname[l] == '/')
 799                 pstrcpy (s, newname + l + 1);
 800             else
 801                 pstrcpy (s, newname + l);
 802         }
 803         else
 804             pstrcpy (s, newname);
 805     }
 806 
 807     dos_ChDir (wd);
 808 
 809     if (strlen (s) == 0)
 810         pstrcpy (s, "./");
 811 
 812     DEBUG (3, ("reduced to %s\n", s));
 813     return (True);
 814 #endif
 815 }
 816 
 817 
 818 /****************************************************************************
 819 expand some *s 
 820 ****************************************************************************/
 821 static void
 822 expand_one (char *Mask, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 823 {
 824     char *p1;
 825     while ((p1 = strchr (Mask, '*')) != NULL)
 826     {
 827         int lfill = (len + 1) - strlen (Mask);
 828         int l1 = (p1 - Mask);
 829         pstring tmp;
 830         pstrcpy (tmp, Mask);
 831         memset (tmp + l1, '?', lfill);
 832         pstrcpy (tmp + l1 + lfill, Mask + l1 + 1);
 833         pstrcpy (Mask, tmp);
 834     }
 835 }
 836 
 837 /****************************************************************************
 838 parse out a directory name from a path name. Assumes dos style filenames.
 839 ****************************************************************************/
 840 static void
 841 dirname_dos (char *path, char *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 842 {
 843     split_at_last_component (path, buf, '\\', NULL);
 844 }
 845 
 846 
 847 /****************************************************************************
 848 parse out a filename from a path name. Assumes dos style filenames.
 849 ****************************************************************************/
 850 static char *
 851 filename_dos (char *path, char *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 852 {
 853     char *p = strrchr (path, '\\');
 854 
 855     if (!p)
 856         pstrcpy (buf, path);
 857     else
 858         pstrcpy (buf, p + 1);
 859 
 860     return (buf);
 861 }
 862 
 863 /****************************************************************************
 864 expand a wildcard expression, replacing *s with ?s
 865 ****************************************************************************/
 866 void
 867 expand_mask (char *Mask, BOOL doext)
     /* [previous][next][first][last][top][bottom][index][help]  */
 868 {
 869     pstring mbeg, mext;
 870     pstring dirpart;
 871     pstring filepart;
 872     BOOL hasdot = False;
 873     char *p1;
 874     BOOL absolute = (*Mask == '\\');
 875 
 876     *mbeg = *mext = *dirpart = *filepart = 0;
 877 
 878     /* parse the directory and filename */
 879     if (strchr (Mask, '\\'))
 880         dirname_dos (Mask, dirpart);
 881 
 882     filename_dos (Mask, filepart);
 883 
 884     pstrcpy (mbeg, filepart);
 885     if ((p1 = strchr (mbeg, '.')) != NULL)
 886     {
 887         hasdot = True;
 888         *p1 = 0;
 889         p1++;
 890         pstrcpy (mext, p1);
 891     }
 892     else
 893     {
 894         pstrcpy (mext, "");
 895         if (strlen (mbeg) > 8)
 896         {
 897             pstrcpy (mext, mbeg + 8);
 898             mbeg[8] = 0;
 899         }
 900     }
 901 
 902     if (*mbeg == 0)
 903         pstrcpy (mbeg, "????????");
 904     if ((*mext == 0) && doext && !hasdot)
 905         pstrcpy (mext, "???");
 906 
 907     if (strequal (mbeg, "*") && *mext == 0)
 908         pstrcpy (mext, "*");
 909 
 910     /* expand *'s */
 911     expand_one (mbeg, 8);
 912     if (*mext)
 913         expand_one (mext, 3);
 914 
 915     pstrcpy (Mask, dirpart);
 916     if (*dirpart || absolute)
 917         pstrcat (Mask, "\\");
 918     pstrcat (Mask, mbeg);
 919     pstrcat (Mask, ".");
 920     pstrcat (Mask, mext);
 921 
 922     DEBUG (6, ("Mask expanded to [%s]\n", Mask));
 923 }
 924 
 925 
 926 /****************************************************************************
 927   make a dir struct
 928 ****************************************************************************/
 929 void
 930 make_dir_struct (char *buf, char *mask, char *fname, SMB_OFF_T size, int mode, time_t date)
     /* [previous][next][first][last][top][bottom][index][help]  */
 931 {
 932     char *p;
 933     pstring mask2;
 934 
 935     pstrcpy (mask2, mask);
 936 
 937     if ((mode & aDIR) != 0)
 938         size = 0;
 939 
 940     memset (buf + 1, ' ', 11);
 941     if ((p = strchr (mask2, '.')) != NULL)
 942     {
 943         *p = 0;
 944         memcpy (buf + 1, mask2, MIN (strlen (mask2), 8));
 945         memcpy (buf + 9, p + 1, MIN (strlen (p + 1), 3));
 946         *p = '.';
 947     }
 948     else
 949         memcpy (buf + 1, mask2, MIN (strlen (mask2), 11));
 950 
 951     memset (buf + 21, '\0', DIR_STRUCT_SIZE - 21);
 952     CVAL (buf, 21) = mode;
 953     put_dos_date (buf, 22, date);
 954     SSVAL (buf, 26, size & 0xFFFF);
 955     SSVAL (buf, 28, (size >> 16) & 0xFFFF);
 956     StrnCpy (buf + 30, fname, 12);
 957     if (!case_sensitive)
 958         strupper (buf + 30);
 959     DEBUG (8, ("put name [%s] into dir struct\n", buf + 30));
 960 }
 961 
 962 
 963 /*******************************************************************
 964 close the low 3 fd's and open dev/null in their place
 965 ********************************************************************/
 966 void
 967 close_low_fds (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 968 {
 969     int fd;
 970     int i;
 971     close (0);
 972     close (1);
 973     close (2);
 974     /* try and use up these file descriptors, so silly
 975        library routines writing to stdout etc won't cause havoc */
 976     for (i = 0; i < 3; i++)
 977     {
 978         fd = sys_open ("/dev/null", O_RDWR, 0);
 979         if (fd < 0)
 980             fd = sys_open ("/dev/null", O_WRONLY, 0);
 981         if (fd < 0)
 982         {
 983             DEBUG (0, ("Cannot open /dev/null\n"));
 984             return;
 985         }
 986         if (fd != i)
 987         {
 988             DEBUG (0, ("Didn't get file descriptor %d\n", i));
 989             return;
 990         }
 991     }
 992 }
 993 #endif /* 0 */
 994 
 995 /****************************************************************************
 996 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
 997 else
 998 if SYSV use O_NDELAY
 999 if BSD use FNDELAY
1000 ****************************************************************************/
1001 int
1002 set_blocking (int fd, BOOL set)
     /* [previous][next][first][last][top][bottom][index][help]  */
1003 {
1004     int val;
1005 #ifdef O_NONBLOCK
1006 #define FLAG_TO_SET O_NONBLOCK
1007 #else
1008 #ifdef SYSV
1009 #define FLAG_TO_SET O_NDELAY
1010 #else /* BSD */
1011 #define FLAG_TO_SET FNDELAY
1012 #endif
1013 #endif
1014 
1015     if ((val = fcntl (fd, F_GETFL, 0)) == -1)
1016         return -1;
1017     if (set)                    /* Turn blocking on - ie. clear nonblock flag */
1018         val &= ~FLAG_TO_SET;
1019     else
1020         val |= FLAG_TO_SET;
1021     return fcntl (fd, F_SETFL, val);
1022 #undef FLAG_TO_SET
1023 }
1024 
1025 
1026 /*******************************************************************
1027 find the difference in milliseconds between two struct timeval
1028 values
1029 ********************************************************************/
1030 int
1031 TvalDiff (struct timeval *tvalold, struct timeval *tvalnew)
     /* [previous][next][first][last][top][bottom][index][help]  */
1032 {
1033     return ((tvalnew->tv_sec - tvalold->tv_sec) * 1000 +
1034             ((int) tvalnew->tv_usec - (int) tvalold->tv_usec) / 1000);
1035 }
1036 
1037 
1038 #if 0
1039 /****************************************************************************
1040 transfer some data between two fd's
1041 ****************************************************************************/
1042 SMB_OFF_T
1043 transfer_file (int infd, int outfd, SMB_OFF_T n, char *header, int headlen, int align)
     /* [previous][next][first][last][top][bottom][index][help]  */
1044 {
1045     static char *buf = NULL;
1046     static int size = 0;
1047     char *buf1, *abuf;
1048     SMB_OFF_T total = 0;
1049 
1050     DEBUG (4, ("transfer_file n=%.0f  (head=%d) called\n", (double) n, headlen));
1051 
1052     if (size == 0)
1053     {
1054         size = lp_readsize ();
1055         size = MAX (size, 1024);
1056     }
1057 
1058     while (!buf && size > 0)
1059     {
1060         buf = (char *) Realloc (buf, size + 8);
1061         if (!buf)
1062             size /= 2;
1063     }
1064 
1065     if (!buf)
1066     {
1067         DEBUG (0, ("Cannot allocate transfer buffer!\n"));
1068         exit (1);
1069     }
1070 
1071     abuf = buf + (align % 8);
1072 
1073     if (header)
1074         n += headlen;
1075 
1076     while (n > 0)
1077     {
1078         int s = (int) MIN (n, (SMB_OFF_T) size);
1079         int ret, ret2 = 0;
1080 
1081         ret = 0;
1082 
1083         if (header && (headlen >= MIN (s, 1024)))
1084         {
1085             buf1 = header;
1086             s = headlen;
1087             ret = headlen;
1088             headlen = 0;
1089             header = NULL;
1090         }
1091         else
1092         {
1093             buf1 = abuf;
1094         }
1095 
1096         if (header && headlen > 0)
1097         {
1098             ret = MIN (headlen, size);
1099             memcpy (buf1, header, ret);
1100             headlen -= ret;
1101             header += ret;
1102             if (headlen <= 0)
1103                 header = NULL;
1104         }
1105 
1106         if (s > ret)
1107             ret += read (infd, buf1 + ret, s - ret);
1108 
1109         if (ret > 0)
1110         {
1111             ret2 = (outfd >= 0 ? write_data (outfd, buf1, ret) : ret);
1112             if (ret2 > 0)
1113                 total += ret2;
1114             /* if we can't write then dump excess data */
1115             if (ret2 != ret)
1116                 transfer_file (infd, -1, n - (ret + headlen), NULL, 0, 0);
1117         }
1118         if (ret <= 0 || ret2 != ret)
1119             return (total);
1120         n -= ret;
1121     }
1122     return (total);
1123 }
1124 
1125 
1126 /****************************************************************************
1127 find a pointer to a netbios name
1128 ****************************************************************************/
1129 static char *
1130 name_ptr (char *buf, int ofs)
     /* [previous][next][first][last][top][bottom][index][help]  */
1131 {
1132     unsigned char c = *(unsigned char *) (buf + ofs);
1133 
1134     if ((c & 0xC0) == 0xC0)
1135     {
1136         uint16 l;
1137         char p[2];
1138         memcpy (p, buf + ofs, 2);
1139         p[0] &= ~0xC0;
1140         l = RSVAL (p, 0);
1141         DEBUG (5, ("name ptr to pos %d from %d is %s\n", l, ofs, buf + l));
1142         return (buf + l);
1143     }
1144     else
1145         return (buf + ofs);
1146 }
1147 
1148 /****************************************************************************
1149 extract a netbios name from a buf
1150 ****************************************************************************/
1151 int
1152 name_extract (char *buf, int ofs, char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
1153 {
1154     char *p = name_ptr (buf, ofs);
1155     int d = PTR_DIFF (p, buf + ofs);
1156     pstrcpy (name, "");
1157     if (d < -50 || d > 50)
1158         return (0);
1159     return (name_interpret (p, name));
1160 }
1161 #endif /* 0 */
1162 
1163 /****************************************************************************
1164 return the total storage length of a mangled name
1165 ****************************************************************************/
1166 int
1167 name_len (char *s1)
     /* [previous][next][first][last][top][bottom][index][help]  */
1168 {
1169     /* NOTE: this argument _must_ be unsigned */
1170     unsigned char *s = (unsigned char *) s1;
1171     int len;
1172 
1173     /* If the two high bits of the byte are set, return 2. */
1174     if (0xC0 == (*s & 0xC0))
1175         return (2);
1176 
1177     /* Add up the length bytes. */
1178     for (len = 1; (*s); s += (*s) + 1)
1179     {
1180         len += *s + 1;
1181         SMB_ASSERT (len < 80);
1182     }
1183 
1184     return (len);
1185 }                               /* name_len */
1186 
1187 
1188 /*******************************************************************
1189 sleep for a specified number of milliseconds
1190 ********************************************************************/
1191 void
1192 msleep (int t)
     /* [previous][next][first][last][top][bottom][index][help]  */
1193 {
1194     int tdiff = 0;
1195     struct timeval tval, t1, t2;
1196     fd_set fds;
1197 
1198     GetTimeOfDay (&t1);
1199     GetTimeOfDay (&t2);
1200 
1201     while (tdiff < t)
1202     {
1203         tval.tv_sec = (t - tdiff) / 1000;
1204         tval.tv_usec = 1000 * ((t - tdiff) % 1000);
1205 
1206         FD_ZERO (&fds);
1207         errno = 0;
1208         sys_select (0, &fds, &tval);
1209 
1210         GetTimeOfDay (&t2);
1211         tdiff = TvalDiff (&t1, &t2);
1212     }
1213 }
1214 
1215 #if 0
1216 /*********************************************************
1217 * Recursive routine that is called by unix_mask_match.
1218 * Does the actual matching. This is the 'original code' 
1219 * used by the unix matcher.
1220 *********************************************************/
1221 static BOOL
1222 unix_do_match (char *str, char *regexp, int case_sig)
     /* [previous][next][first][last][top][bottom][index][help]  */
1223 {
1224     char *p;
1225 
1226     for (p = regexp; *p && *str;)
1227     {
1228         switch (*p)
1229         {
1230         case '?':
1231             str++;
1232             p++;
1233             break;
1234 
1235         case '*':
1236             /* Look for a character matching 
1237                the one after the '*' */
1238             p++;
1239             if (!*p)
1240                 return True;    /* Automatic match */
1241             while (*str)
1242             {
1243                 while (*str && (case_sig ? (*p != *str) : (toupper (*p) != toupper (*str))))
1244                     str++;
1245                 if (unix_do_match (str, p, case_sig))
1246                     return True;
1247                 if (!*str)
1248                     return False;
1249                 else
1250                     str++;
1251             }
1252             return False;
1253 
1254         default:
1255             if (case_sig)
1256             {
1257                 if (*str != *p)
1258                     return False;
1259             }
1260             else
1261             {
1262                 if (toupper (*str) != toupper (*p))
1263                     return False;
1264             }
1265             str++, p++;
1266             break;
1267         }
1268     }
1269     if (!*p && !*str)
1270         return True;
1271 
1272     if (!*p && str[0] == '.' && str[1] == 0)
1273         return (True);
1274 
1275     if (!*str && *p == '?')
1276     {
1277         while (*p == '?')
1278             p++;
1279         return (!*p);
1280     }
1281 
1282     if (!*str && (*p == '*' && p[1] == '\0'))
1283         return True;
1284     return False;
1285 }
1286 
1287 
1288 /*********************************************************
1289 * Routine to match a given string with a regexp - uses
1290 * simplified regexp that takes * and ? only. Case can be
1291 * significant or not.
1292 * This is the 'original code' used by the unix matcher.
1293 *********************************************************/
1294 
1295 static BOOL
1296 unix_mask_match (char *str, char *regexp, int case_sig, BOOL trans2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1297 {
1298     char *p;
1299     pstring p1, p2;
1300     fstring ebase, eext, sbase, sext;
1301 
1302     BOOL matched;
1303 
1304     /* Make local copies of str and regexp */
1305     StrnCpy (p1, regexp, sizeof (pstring) - 1);
1306     StrnCpy (p2, str, sizeof (pstring) - 1);
1307 
1308     if (!strchr (p2, '.'))
1309     {
1310         pstrcat (p2, ".");
1311     }
1312 
1313     /* Remove any *? and ** as they are meaningless */
1314     for (p = p1; *p; p++)
1315         while (*p == '*' && (p[1] == '?' || p[1] == '*'))
1316             (void) pstrcpy (&p[1], &p[2]);
1317 
1318     if (strequal (p1, "*"))
1319         return (True);
1320 
1321     DEBUG (8, ("unix_mask_match str=<%s> regexp=<%s>, case_sig = %d\n", p2, p1, case_sig));
1322 
1323     if (trans2)
1324     {
1325         fstrcpy (ebase, p1);
1326         fstrcpy (sbase, p2);
1327     }
1328     else
1329     {
1330         if ((p = strrchr (p1, '.')))
1331         {
1332             *p = 0;
1333             fstrcpy (ebase, p1);
1334             fstrcpy (eext, p + 1);
1335         }
1336         else
1337         {
1338             fstrcpy (ebase, p1);
1339             eext[0] = 0;
1340         }
1341 
1342         if (!strequal (p2, ".") && !strequal (p2, "..") && (p = strrchr (p2, '.')))
1343         {
1344             *p = 0;
1345             fstrcpy (sbase, p2);
1346             fstrcpy (sext, p + 1);
1347         }
1348         else
1349         {
1350             fstrcpy (sbase, p2);
1351             fstrcpy (sext, "");
1352         }
1353     }
1354 
1355     matched = unix_do_match (sbase, ebase, case_sig) &&
1356         (trans2 || unix_do_match (sext, eext, case_sig));
1357 
1358     DEBUG (8, ("unix_mask_match returning %d\n", matched));
1359 
1360     return matched;
1361 }
1362 
1363 /*********************************************************
1364 * Recursive routine that is called by mask_match.
1365 * Does the actual matching. Returns True if matched,
1366 * False if failed. This is the 'new' NT style matcher.
1367 *********************************************************/
1368 
1369 BOOL
1370 do_match (char *str, char *regexp, int case_sig)
     /* [previous][next][first][last][top][bottom][index][help]  */
1371 {
1372     char *p;
1373 
1374     for (p = regexp; *p && *str;)
1375     {
1376         switch (*p)
1377         {
1378         case '?':
1379             str++;
1380             p++;
1381             break;
1382 
1383         case '*':
1384             /* Look for a character matching 
1385                the one after the '*' */
1386             p++;
1387             if (!*p)
1388                 return True;    /* Automatic match */
1389             while (*str)
1390             {
1391                 while (*str && (case_sig ? (*p != *str) : (toupper (*p) != toupper (*str))))
1392                     str++;
1393                 /* Now eat all characters that match, as
1394                    we want the *last* character to match. */
1395                 while (*str && (case_sig ? (*p == *str) : (toupper (*p) == toupper (*str))))
1396                     str++;
1397                 str--;          /* We've eaten the match char after the '*' */
1398                 if (do_match (str, p, case_sig))
1399                 {
1400                     return True;
1401                 }
1402                 if (!*str)
1403                 {
1404                     return False;
1405                 }
1406                 else
1407                 {
1408                     str++;
1409                 }
1410             }
1411             return False;
1412 
1413         default:
1414             if (case_sig)
1415             {
1416                 if (*str != *p)
1417                 {
1418                     return False;
1419                 }
1420             }
1421             else
1422             {
1423                 if (toupper (*str) != toupper (*p))
1424                 {
1425                     return False;
1426                 }
1427             }
1428             str++, p++;
1429             break;
1430         }
1431     }
1432 
1433     if (!*p && !*str)
1434         return True;
1435 
1436     if (!*p && str[0] == '.' && str[1] == 0)
1437     {
1438         return (True);
1439     }
1440 
1441     if (!*str && *p == '?')
1442     {
1443         while (*p == '?')
1444             p++;
1445         return (!*p);
1446     }
1447 
1448     if (!*str && (*p == '*' && p[1] == '\0'))
1449     {
1450         return True;
1451     }
1452 
1453     return False;
1454 }
1455 
1456 
1457 /*********************************************************
1458 * Routine to match a given string with a regexp - uses
1459 * simplified regexp that takes * and ? only. Case can be
1460 * significant or not.
1461 * The 8.3 handling was rewritten by Ums Harald <Harald.Ums@pro-sieben.de>
1462 * This is the new 'NT style' matcher.
1463 *********************************************************/
1464 
1465 BOOL
1466 mask_match (char *str, char *regexp, int case_sig, BOOL trans2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1467 {
1468     char *p;
1469     pstring t_pattern, t_filename, te_pattern, te_filename;
1470     fstring ebase, eext, sbase, sext;
1471 
1472     BOOL matched = False;
1473 
1474     /* Make local copies of str and regexp */
1475     pstrcpy (t_pattern, regexp);
1476     pstrcpy (t_filename, str);
1477 
1478     if (trans2)
1479     {
1480 
1481         /* a special case for 16 bit apps */
1482         if (strequal (t_pattern, "????????.???"))
1483             pstrcpy (t_pattern, "*");
1484 
1485 #if 0
1486         /*
1487          * Handle broken clients that send us old 8.3 format.
1488          */
1489         string_sub (t_pattern, "????????", "*");
1490         string_sub (t_pattern, ".???", ".*");
1491 #endif
1492     }
1493 
1494 #if 0
1495     /* 
1496      * Not sure if this is a good idea. JRA.
1497      */
1498     if (trans2 && is_8_3 (t_pattern, False) && is_8_3 (t_filename, False))
1499         trans2 = False;
1500 #endif
1501 
1502 #if 0
1503     if (!strchr (t_filename, '.'))
1504     {
1505         pstrcat (t_filename, ".");
1506     }
1507 #endif
1508 
1509     /* Remove any *? and ** as they are meaningless */
1510     string_sub (t_pattern, "*?", "*");
1511     string_sub (t_pattern, "**", "*");
1512 
1513     if (strequal (t_pattern, "*"))
1514         return (True);
1515 
1516     DEBUG (8,
1517            ("mask_match str=<%s> regexp=<%s>, case_sig = %d\n", t_filename, t_pattern, case_sig));
1518 
1519     if (trans2)
1520     {
1521         /*
1522          * Match each component of the regexp, split up by '.'
1523          * characters.
1524          */
1525         char *fp, *rp, *cp2, *cp1;
1526         BOOL last_wcard_was_star = False;
1527         int num_path_components, num_regexp_components;
1528 
1529         pstrcpy (te_pattern, t_pattern);
1530         pstrcpy (te_filename, t_filename);
1531         /*
1532          * Remove multiple "*." patterns.
1533          */
1534         string_sub (te_pattern, "*.*.", "*.");
1535         num_regexp_components = count_chars (te_pattern, '.');
1536         num_path_components = count_chars (te_filename, '.');
1537 
1538         /* 
1539          * Check for special 'hack' case of "DIR a*z". - needs to match a.b.c...z
1540          */
1541         if (num_regexp_components == 0)
1542             matched = do_match (te_filename, te_pattern, case_sig);
1543         else
1544         {
1545             for (cp1 = te_pattern, cp2 = te_filename; cp1;)
1546             {
1547                 fp = strchr (cp2, '.');
1548                 if (fp)
1549                     *fp = '\0';
1550                 rp = strchr (cp1, '.');
1551                 if (rp)
1552                     *rp = '\0';
1553 
1554                 if (cp1[strlen (cp1) - 1] == '*')
1555                     last_wcard_was_star = True;
1556                 else
1557                     last_wcard_was_star = False;
1558 
1559                 if (!do_match (cp2, cp1, case_sig))
1560                     break;
1561 
1562                 cp1 = rp ? rp + 1 : NULL;
1563                 cp2 = fp ? fp + 1 : "";
1564 
1565                 if (last_wcard_was_star || ((cp1 != NULL) && (*cp1 == '*')))
1566                 {
1567                     /* Eat the extra path components. */
1568                     int i;
1569 
1570                     for (i = 0; i < num_path_components - num_regexp_components; i++)
1571                     {
1572                         fp = strchr (cp2, '.');
1573                         if (fp)
1574                             *fp = '\0';
1575 
1576                         if ((cp1 != NULL) && do_match (cp2, cp1, case_sig))
1577                         {
1578                             cp2 = fp ? fp + 1 : "";
1579                             break;
1580                         }
1581                         cp2 = fp ? fp + 1 : "";
1582                     }
1583                     num_path_components -= i;
1584                 }
1585             }
1586             if (cp1 == NULL && ((*cp2 == '\0') || last_wcard_was_star))
1587                 matched = True;
1588         }
1589     }
1590     else
1591     {
1592 
1593         /* -------------------------------------------------
1594          * Behaviour of Win95
1595          * for 8.3 filenames and 8.3 Wildcards
1596          * -------------------------------------------------
1597          */
1598         if (strequal (t_filename, "."))
1599         {
1600             /*
1601              *  Patterns:  *.*  *. ?. ? ????????.??? are valid.
1602              * 
1603              */
1604             if (strequal (t_pattern, "*.*") || strequal (t_pattern, "*.") ||
1605                 strequal (t_pattern, "????????.???") ||
1606                 strequal (t_pattern, "?.") || strequal (t_pattern, "?"))
1607                 matched = True;
1608         }
1609         else if (strequal (t_filename, ".."))
1610         {
1611             /*
1612              *  Patterns:  *.*  *. ?. ? *.? ????????.??? are valid.
1613              *
1614              */
1615             if (strequal (t_pattern, "*.*") || strequal (t_pattern, "*.") ||
1616                 strequal (t_pattern, "?.") || strequal (t_pattern, "?") ||
1617                 strequal (t_pattern, "????????.???") ||
1618                 strequal (t_pattern, "*.?") || strequal (t_pattern, "?.*"))
1619                 matched = True;
1620         }
1621         else
1622         {
1623 
1624             if ((p = strrchr (t_pattern, '.')))
1625             {
1626                 /*
1627                  * Wildcard has a suffix.
1628                  */
1629                 *p = 0;
1630                 fstrcpy (ebase, t_pattern);
1631                 if (p[1])
1632                 {
1633                     fstrcpy (eext, p + 1);
1634                 }
1635                 else
1636                 {
1637                     /* pattern ends in DOT: treat as if there is no DOT */
1638                     *eext = 0;
1639                     if (strequal (ebase, "*"))
1640                         return (True);
1641                 }
1642             }
1643             else
1644             {
1645                 /*
1646                  * No suffix for wildcard.
1647                  */
1648                 fstrcpy (ebase, t_pattern);
1649                 eext[0] = 0;
1650             }
1651 
1652             p = strrchr (t_filename, '.');
1653             if (p && (p[1] == 0))
1654             {
1655                 /*
1656                  * Filename has an extension of '.' only.
1657                  */
1658                 *p = 0;         /* nuke dot at end of string */
1659                 p = 0;          /* and treat it as if there is no extension */
1660             }
1661 
1662             if (p)
1663             {
1664                 /*
1665                  * Filename has an extension.
1666                  */
1667                 *p = 0;
1668                 fstrcpy (sbase, t_filename);
1669                 fstrcpy (sext, p + 1);
1670                 if (*eext)
1671                 {
1672                     matched = do_match (sbase, ebase, case_sig) && do_match (sext, eext, case_sig);
1673                 }
1674                 else
1675                 {
1676                     /* pattern has no extension */
1677                     /* Really: match complete filename with pattern ??? means exactly 3 chars */
1678                     matched = do_match (str, ebase, case_sig);
1679                 }
1680             }
1681             else
1682             {
1683                 /* 
1684                  * Filename has no extension.
1685                  */
1686                 fstrcpy (sbase, t_filename);
1687                 fstrcpy (sext, "");
1688                 if (*eext)
1689                 {
1690                     /* pattern has extension */
1691                     matched = do_match (sbase, ebase, case_sig) && do_match (sext, eext, case_sig);
1692                 }
1693                 else
1694                 {
1695                     matched = do_match (sbase, ebase, case_sig);
1696 #ifdef EMULATE_WEIRD_W95_MATCHING
1697                     /*
1698                      * Even Microsoft has some problems
1699                      * Behaviour Win95 -> local disk 
1700                      * is different from Win95 -> smb drive from Nt 4.0
1701                      * This branch would reflect the Win95 local disk behaviour
1702                      */
1703                     if (!matched)
1704                     {
1705                         /* a? matches aa and a in w95 */
1706                         fstrcat (sbase, ".");
1707                         matched = do_match (sbase, ebase, case_sig);
1708                     }
1709 #endif
1710                 }
1711             }
1712         }
1713     }
1714 
1715     DEBUG (8, ("mask_match returning %d\n", matched));
1716 
1717     return matched;
1718 }
1719 #endif /* 0 */
1720 
1721 #if 0
1722 /****************************************************************************
1723 set the length of a file from a filedescriptor.
1724 Returns 0 on success, -1 on failure.
1725 ****************************************************************************/
1726 
1727 int
1728 set_filelen (int fd, SMB_OFF_T len)
     /* [previous][next][first][last][top][bottom][index][help]  */
1729 {
1730     /* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot
1731        extend a file with ftruncate. Provide alternate implementation
1732        for this */
1733 
1734 #ifdef HAVE_FTRUNCATE_EXTEND
1735     return sys_ftruncate (fd, len);
1736 #else
1737     SMB_STRUCT_STAT st;
1738     char c = 0;
1739     SMB_OFF_T currpos = sys_lseek (fd, (SMB_OFF_T) 0, SEEK_CUR);
1740 
1741     if (currpos == -1)
1742         return -1;
1743     /* Do an fstat to see if the file is longer than
1744        the requested size (call ftruncate),
1745        or shorter, in which case seek to len - 1 and write 1
1746        byte of zero */
1747     if (sys_fstat (fd, &st) < 0)
1748         return -1;
1749 
1750 #ifdef S_ISFIFO
1751     if (S_ISFIFO (st.st_mode))
1752         return 0;
1753 #endif
1754 
1755     if (st.st_size == len)
1756         return 0;
1757     if (st.st_size > len)
1758         return sys_ftruncate (fd, len);
1759 
1760     if (sys_lseek (fd, len - 1, SEEK_SET) != len - 1)
1761         return -1;
1762     if (write (fd, &c, 1) != 1)
1763         return -1;
1764     /* Seek to where we were */
1765     if (sys_lseek (fd, currpos, SEEK_SET) != currpos)
1766         return -1;
1767     return 0;
1768 #endif
1769 }
1770 
1771 
1772 #ifdef HPUX
1773 /****************************************************************************
1774 this is a version of setbuffer() for those machines that only have setvbuf
1775 ****************************************************************************/
1776 void
1777 setbuffer (FILE * f, char *buf, int bufsize)
     /* [previous][next][first][last][top][bottom][index][help]  */
1778 {
1779     setvbuf (f, buf, _IOFBF, bufsize);
1780 }
1781 #endif
1782 #endif /* 0 */
1783 
1784 
1785 
1786 /****************************************************************************
1787 expand a pointer to be a particular size
1788 ****************************************************************************/
1789 void *
1790 Realloc (void *p, size_t size)
     /* [previous][next][first][last][top][bottom][index][help]  */
1791 {
1792     void *ret = NULL;
1793 
1794     if (size == 0)
1795     {
1796         if (p)
1797             free (p);
1798         DEBUG (5, ("Realloc asked for 0 bytes\n"));
1799         return NULL;
1800     }
1801 
1802     if (!p)
1803         ret = (void *) malloc (size);
1804     else
1805         ret = (void *) realloc (p, size);
1806 
1807 #ifdef MEM_MAN
1808     {
1809         extern FILE *dbf;
1810         smb_mem_write_info (ret, dbf);
1811     }
1812 #endif
1813 
1814     if (!ret)
1815         DEBUG (0, ("Memory allocation error: failed to expand to %d bytes\n", (int) size));
1816 
1817     return (ret);
1818 }
1819 
1820 
1821 /****************************************************************************
1822 get my own name and IP
1823 ****************************************************************************/
1824 BOOL
1825 get_myname (char *my_name, struct in_addr * ip)
     /* [previous][next][first][last][top][bottom][index][help]  */
1826 {
1827     struct hostent *hp;
1828     pstring hostname;
1829 
1830     /* cppcheck-suppress uninitvar */
1831     *hostname = 0;
1832 
1833     /* get my host name */
1834     if (gethostname (hostname, sizeof (hostname)) == -1)
1835     {
1836         DEBUG (0, ("gethostname failed\n"));
1837         return False;
1838     }
1839 
1840     /* Ensure null termination. */
1841     hostname[sizeof (hostname) - 1] = '\0';
1842 
1843     /* get host info */
1844     if ((hp = Get_Hostbyname (hostname)) == 0)
1845     {
1846         DEBUG (0, ("Get_Hostbyname: Unknown host %s\n", hostname));
1847         return False;
1848     }
1849 
1850     if (my_name)
1851     {
1852         /* split off any parts after an initial . */
1853         char *p = strchr (hostname, '.');
1854         if (p)
1855             *p = 0;
1856 
1857         fstrcpy (my_name, hostname);
1858     }
1859 
1860     if (ip)
1861         putip ((char *) ip, (char *) hp->h_addr);
1862 
1863     return (True);
1864 }
1865 
1866 
1867 /****************************************************************************
1868 true if two IP addresses are equal
1869 ****************************************************************************/
1870 BOOL
1871 ip_equal (struct in_addr ip1, struct in_addr ip2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1872 {
1873     uint32 a1, a2;
1874     a1 = ntohl (ip1.s_addr);
1875     a2 = ntohl (ip2.s_addr);
1876     return (a1 == a2);
1877 }
1878 
1879 #if 0                           /* May be useful one day */
1880 /****************************************************************************
1881 interpret a protocol description string, with a default
1882 ****************************************************************************/
1883 int
1884 interpret_protocol (char *str, int def)
     /* [previous][next][first][last][top][bottom][index][help]  */
1885 {
1886     if (strequal (str, "NT1"))
1887         return (PROTOCOL_NT1);
1888     if (strequal (str, "LANMAN2"))
1889         return (PROTOCOL_LANMAN2);
1890     if (strequal (str, "LANMAN1"))
1891         return (PROTOCOL_LANMAN1);
1892     if (strequal (str, "CORE"))
1893         return (PROTOCOL_CORE);
1894     if (strequal (str, "COREPLUS"))
1895         return (PROTOCOL_COREPLUS);
1896     if (strequal (str, "CORE+"))
1897         return (PROTOCOL_COREPLUS);
1898 
1899     DEBUG (0, ("Unrecognised protocol level %s\n", str));
1900 
1901     return (def);
1902 }
1903 #endif /* 0 */
1904 
1905 /****************************************************************************
1906 interpret an internet address or name into an IP address in 4 byte form
1907 ****************************************************************************/
1908 uint32
1909 interpret_addr (const char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
1910 {
1911     struct hostent *hp;
1912     uint32 res;
1913     int i;
1914     BOOL pure_address = True;
1915 
1916     if (strcmp (str, "0.0.0.0") == 0)
1917         return (0);
1918     if (strcmp (str, "255.255.255.255") == 0)
1919         return (0xFFFFFFFF);
1920 
1921     for (i = 0; pure_address && str[i]; i++)
1922         if (!(isdigit ((int) str[i]) || str[i] == '.'))
1923             pure_address = False;
1924 
1925     /* if it's in the form of an IP address then get the lib to interpret it */
1926     if (pure_address)
1927     {
1928         res = inet_addr (str);
1929     }
1930     else
1931     {
1932         /* otherwise assume it's a network name of some sort and use 
1933            Get_Hostbyname */
1934         if ((hp = Get_Hostbyname (str)) == 0)
1935         {
1936             DEBUG (3, ("Get_Hostbyname: Unknown host. %s\n", str));
1937             return 0;
1938         }
1939         if (hp->h_addr == NULL)
1940         {
1941             DEBUG (3, ("Get_Hostbyname: host address is invalid for host %s\n", str));
1942             return 0;
1943         }
1944         putip ((char *) &res, (char *) hp->h_addr);
1945     }
1946 
1947     if (res == (uint32) - 1)
1948         return (0);
1949 
1950     return (res);
1951 }
1952 
1953 /*******************************************************************
1954   a convenient addition to interpret_addr()
1955   ******************************************************************/
1956 struct in_addr *
1957 interpret_addr2 (const char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
1958 {
1959     static struct in_addr ret;
1960     uint32 a = interpret_addr (str);
1961     ret.s_addr = a;
1962     return (&ret);
1963 }
1964 
1965 /*******************************************************************
1966   check if an IP is the 0.0.0.0
1967   ******************************************************************/
1968 BOOL
1969 zero_ip (struct in_addr ip)
     /* [previous][next][first][last][top][bottom][index][help]  */
1970 {
1971     uint32 a;
1972     putip ((char *) &a, (char *) &ip);
1973     return (a == 0);
1974 }
1975 
1976 
1977 /*******************************************************************
1978  matchname - determine if host name matches IP address 
1979  ******************************************************************/
1980 BOOL
1981 matchname (char *remotehost, struct in_addr addr)
     /* [previous][next][first][last][top][bottom][index][help]  */
1982 {
1983     struct hostent *hp;
1984     int i;
1985 
1986     if ((hp = Get_Hostbyname (remotehost)) == 0)
1987     {
1988         DEBUG (0, ("Get_Hostbyname(%s): lookup failure.\n", remotehost));
1989         return False;
1990     }
1991 
1992     /*
1993      * Make sure that gethostbyname() returns the "correct" host name.
1994      * Unfortunately, gethostbyname("localhost") sometimes yields
1995      * "localhost.domain". Since the latter host name comes from the
1996      * local DNS, we just have to trust it (all bets are off if the local
1997      * DNS is perverted). We always check the address list, though.
1998      */
1999 
2000     if (strcasecmp (remotehost, hp->h_name) && strcasecmp (remotehost, "localhost"))
2001     {
2002         DEBUG (0, ("host name/name mismatch: %s != %s\n", remotehost, hp->h_name));
2003         return False;
2004     }
2005 
2006     /* Look up the host address in the address list we just got. */
2007     for (i = 0; hp->h_addr_list[i]; i++)
2008     {
2009         if (memcmp (hp->h_addr_list[i], (caddr_t) & addr, sizeof (addr)) == 0)
2010             return True;
2011     }
2012 
2013     /*
2014      * The host name does not map to the original host address. Perhaps
2015      * someone has compromised a name server. More likely someone botched
2016      * it, but that could be dangerous, too.
2017      */
2018 
2019     DEBUG (0, ("host name/address mismatch: %s != %s\n", inet_ntoa (addr), hp->h_name));
2020     return False;
2021 }
2022 
2023 
2024 #if (defined(HAVE_NETGROUP) && defined(WITH_AUTOMOUNT))
2025 /******************************************************************
2026  Remove any mount options such as -rsize=2048,wsize=2048 etc.
2027  Based on a fix from <Thomas.Hepper@icem.de>.
2028 *******************************************************************/
2029 
2030 static void
2031 strip_mount_options (pstring * str)
     /* [previous][next][first][last][top][bottom][index][help]  */
2032 {
2033     if (**str == '-')
2034     {
2035         char *p = *str;
2036         while (*p && !isspace (*p))
2037             p++;
2038         while (*p && isspace (*p))
2039             p++;
2040         if (*p)
2041         {
2042             pstring tmp_str;
2043 
2044             pstrcpy (tmp_str, p);
2045             pstrcpy (*str, tmp_str);
2046         }
2047     }
2048 }
2049 
2050 /*******************************************************************
2051  Patch from jkf@soton.ac.uk
2052  Split Luke's automount_server into YP lookup and string splitter
2053  so can easily implement automount_path(). 
2054  As we may end up doing both, cache the last YP result. 
2055 *******************************************************************/
2056 
2057 #ifdef WITH_NISPLUS_HOME
2058 static char *
2059 automount_lookup (char *user_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
2060 {
2061     static fstring last_key = "";
2062     static pstring last_value = "";
2063 
2064     char *nis_map = (char *) lp_nis_home_map_name ();
2065 
2066     char buffer[NIS_MAXATTRVAL + 1];
2067     nis_result *result;
2068     nis_object *object;
2069     entry_obj *entry;
2070 
2071     DEBUG (5, ("NIS+ Domain: %s\n", (char *) nis_local_directory ()));
2072 
2073     if (strcmp (user_name, last_key))
2074     {
2075         slprintf (buffer, sizeof (buffer) - 1, "[%s=%s]%s.%s", "key", user_name, nis_map,
2076                   (char *) nis_local_directory ());
2077         DEBUG (5, ("NIS+ querystring: %s\n", buffer));
2078 
2079         if (result = nis_list (buffer, RETURN_RESULT, NULL, NULL))
2080         {
2081             if (result->status != NIS_SUCCESS)
2082             {
2083                 DEBUG (3, ("NIS+ query failed: %s\n", nis_sperrno (result->status)));
2084                 fstrcpy (last_key, "");
2085                 pstrcpy (last_value, "");
2086             }
2087             else
2088             {
2089                 object = result->objects.objects_val;
2090                 if (object->zo_data.zo_type == ENTRY_OBJ)
2091                 {
2092                     entry = &object->zo_data.objdata_u.en_data;
2093                     DEBUG (5, ("NIS+ entry type: %s\n", entry->en_type));
2094                     DEBUG (3,
2095                            ("NIS+ result: %s\n",
2096                             entry->en_cols.en_cols_val[1].ec_value.ec_value_val));
2097 
2098                     pstrcpy (last_value, entry->en_cols.en_cols_val[1].ec_value.ec_value_val);
2099                     string_sub (last_value, "&", user_name);
2100                     fstrcpy (last_key, user_name);
2101                 }
2102             }
2103         }
2104         nis_freeresult (result);
2105     }
2106 
2107     strip_mount_options (&last_value);
2108 
2109     DEBUG (4, ("NIS+ Lookup: %s resulted in %s\n", user_name, last_value));
2110     return last_value;
2111 }
2112 #else /* WITH_NISPLUS_HOME */
2113 static char *
2114 automount_lookup (char *user_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
2115 {
2116     static fstring last_key = "";
2117     static pstring last_value = "";
2118 
2119     int nis_error;              /* returned by yp all functions */
2120     char *nis_result;           /* yp_match inits this */
2121     int nis_result_len;         /* and set this */
2122     char *nis_domain;           /* yp_get_default_domain inits this */
2123     char *nis_map = (char *) lp_nis_home_map_name ();
2124 
2125     if ((nis_error = yp_get_default_domain (&nis_domain)) != 0)
2126     {
2127         DEBUG (3, ("YP Error: %s\n", yperr_string (nis_error)));
2128         return last_value;
2129     }
2130 
2131     DEBUG (5, ("NIS Domain: %s\n", nis_domain));
2132 
2133     if (!strcmp (user_name, last_key))
2134     {
2135         nis_result = last_value;
2136         nis_result_len = strlen (last_value);
2137         nis_error = 0;
2138     }
2139     else
2140     {
2141         if ((nis_error = yp_match (nis_domain, nis_map,
2142                                    user_name, strlen (user_name),
2143                                    &nis_result, &nis_result_len)) != 0)
2144         {
2145             DEBUG (3, ("YP Error: \"%s\" while looking up \"%s\" in map \"%s\"\n",
2146                        yperr_string (nis_error), user_name, nis_map));
2147         }
2148         if (!nis_error && nis_result_len >= sizeof (pstring))
2149         {
2150             nis_result_len = sizeof (pstring) - 1;
2151         }
2152         fstrcpy (last_key, user_name);
2153         strncpy (last_value, nis_result, nis_result_len);
2154         last_value[nis_result_len] = '\0';
2155     }
2156 
2157     strip_mount_options (&last_value);
2158 
2159     DEBUG (4, ("YP Lookup: %s resulted in %s\n", user_name, last_value));
2160     return last_value;
2161 }
2162 #endif /* WITH_NISPLUS_HOME */
2163 #endif
2164 
2165 /*******************************************************************
2166  Patch from jkf@soton.ac.uk
2167  This is Luke's original function with the NIS lookup code
2168  moved out to a separate function.
2169 *******************************************************************/
2170 static char *
2171 automount_server (const char *user_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
2172 {
2173     static pstring server_name;
2174     (void) user_name;
2175 
2176     /* use the local machine name as the default */
2177     /* this will be the default if WITH_AUTOMOUNT is not used or fails */
2178     pstrcpy (server_name, local_machine);
2179 
2180 #if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT))
2181 
2182     if (lp_nis_home_map ())
2183     {
2184         int home_server_len;
2185         char *automount_value = automount_lookup (user_name);
2186         home_server_len = strcspn (automount_value, ":");
2187         DEBUG (5, ("NIS lookup succeeded.  Home server length: %d\n", home_server_len));
2188         if (home_server_len > sizeof (pstring))
2189         {
2190             home_server_len = sizeof (pstring);
2191         }
2192         strncpy (server_name, automount_value, home_server_len);
2193         server_name[home_server_len] = '\0';
2194     }
2195 #endif
2196 
2197     DEBUG (4, ("Home server: %s\n", server_name));
2198 
2199     return server_name;
2200 }
2201 
2202 /*******************************************************************
2203  Patch from jkf@soton.ac.uk
2204  Added this to implement %p (NIS auto-map version of %H)
2205 *******************************************************************/
2206 static char *
2207 automount_path (char *user_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
2208 {
2209     static pstring server_path;
2210 
2211     /* use the passwd entry as the default */
2212     /* this will be the default if WITH_AUTOMOUNT is not used or fails */
2213     /* pstrcpy() copes with get_home_dir() returning NULL */
2214     pstrcpy (server_path, get_home_dir (user_name));
2215 
2216 #if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT))
2217 
2218     if (lp_nis_home_map ())
2219     {
2220         char *home_path_start;
2221         char *automount_value = automount_lookup (user_name);
2222         home_path_start = strchr (automount_value, ':');
2223         if (home_path_start != NULL)
2224         {
2225             DEBUG (5, ("NIS lookup succeeded.  Home path is: %s\n",
2226                        home_path_start ? (home_path_start + 1) : ""));
2227             pstrcpy (server_path, home_path_start + 1);
2228         }
2229     }
2230 #endif
2231 
2232     DEBUG (4, ("Home server path: %s\n", server_path));
2233 
2234     return server_path;
2235 }
2236 
2237 
2238 /*******************************************************************
2239 sub strings with useful parameters
2240 Rewritten by Stefaan A Eeckels <Stefaan.Eeckels@ecc.lu> and
2241 Paul Rippin <pr3245@nopc.eurostat.cec.be>
2242 ********************************************************************/
2243 void
2244 standard_sub_basic (char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
2245 {
2246     char *s, *p;
2247     char pidstr[10];
2248     struct passwd *pass;
2249     const char *username = sam_logon_in_ssb ? samlogon_user : sesssetup_user;
2250 
2251     for (s = str; s && *s && (p = strchr (s, '%')); s = p)
2252     {
2253         switch (*(p + 1))
2254         {
2255         case 'G':
2256             {
2257                 if ((pass = Get_Pwnam (username)) != NULL)
2258                 {
2259                     string_sub (p, "%G", gidtoname (pass->pw_gid));
2260                 }
2261                 else
2262                 {
2263                     p += 2;
2264                 }
2265                 break;
2266             }
2267         case 'N':
2268             string_sub (p, "%N", automount_server (username));
2269             break;
2270         case 'I':
2271             string_sub (p, "%I", client_addr (Client));
2272             break;
2273         case 'L':
2274             string_sub (p, "%L", local_machine);
2275             break;
2276         case 'M':
2277             string_sub (p, "%M", client_name (Client));
2278             break;
2279         case 'R':
2280             string_sub (p, "%R", remote_proto);
2281             break;
2282         case 'T':
2283             string_sub (p, "%T", timestring ());
2284             break;
2285         case 'U':
2286             string_sub (p, "%U", username);
2287             break;
2288         case 'a':
2289             string_sub (p, "%a", remote_arch);
2290             break;
2291         case 'd':
2292             {
2293                 slprintf (pidstr, sizeof (pidstr) - 1, "%d", (int) getpid ());
2294                 string_sub (p, "%d", pidstr);
2295                 break;
2296             }
2297         case 'h':
2298             string_sub (p, "%h", myhostname);
2299             break;
2300         case 'm':
2301             string_sub (p, "%m", remote_machine);
2302             break;
2303         case 'v':
2304             string_sub (p, "%v", VERSION);
2305             break;
2306         case '$':              /* Expand environment variables */
2307             {
2308                 /* Contributed by Branko Cibej <branko.cibej@hermes.si> */
2309                 fstring envname;
2310                 char *envval;
2311                 char *q, *r;
2312                 int copylen;
2313 
2314                 if (*(p + 2) != '(')
2315                 {
2316                     p += 2;
2317                     break;
2318                 }
2319                 if ((q = strchr (p, ')')) == NULL)
2320                 {
2321                     DEBUG (0, ("standard_sub_basic: Unterminated environment \
2322                                         variable [%s]\n", p));
2323                     p += 2;
2324                     break;
2325                 }
2326 
2327                 r = p + 3;
2328                 copylen = MIN ((size_t) (q - r), (size_t) (sizeof (envname) - 1));
2329                 strncpy (envname, r, copylen);
2330                 envname[copylen] = '\0';
2331 
2332                 if ((envval = getenv (envname)) == NULL)
2333                 {
2334                     DEBUG (0, ("standard_sub_basic: Environment variable [%s] not set\n", envname));
2335                     p += 2;
2336                     break;
2337                 }
2338 
2339                 copylen = MIN ((size_t) (q + 1 - p), (size_t) (sizeof (envname) - 1));
2340                 strncpy (envname, p, copylen);
2341                 envname[copylen] = '\0';
2342                 string_sub (p, envname, envval);
2343                 break;
2344             }
2345         case '\0':
2346             p++;
2347             break;              /* don't run off end if last character is % */
2348         default:
2349             p += 2;
2350             break;
2351         }
2352     }
2353     return;
2354 }
2355 
2356 
2357 /****************************************************************************
2358 do some standard substitutions in a string
2359 ****************************************************************************/
2360 void
2361 standard_sub (connection_struct * conn, char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
2362 {
2363     char *p, *s;
2364     const char *home;
2365 
2366     for (s = str; (p = strchr (s, '%')); s = p)
2367     {
2368         switch (*(p + 1))
2369         {
2370         case 'H':
2371             if ((home = get_home_dir (conn->user)))
2372             {
2373                 string_sub (p, "%H", home);
2374             }
2375             else
2376             {
2377                 p += 2;
2378             }
2379             break;
2380 
2381         case 'P':
2382             string_sub (p, "%P", conn->connectpath);
2383             break;
2384 
2385         case 'S':
2386             string_sub (p, "%S", lp_servicename (SNUM (conn)));
2387             break;
2388 
2389         case 'g':
2390             string_sub (p, "%g", gidtoname (conn->gid));
2391             break;
2392         case 'u':
2393             string_sub (p, "%u", conn->user);
2394             break;
2395 
2396             /* Patch from jkf@soton.ac.uk Left the %N (NIS
2397              * server name) in standard_sub_basic as it is
2398              * a feature for logon servers, hence uses the
2399              * username.  The %p (NIS server path) code is
2400              * here as it is used instead of the default
2401              * "path =" string in [homes] and so needs the
2402              * service name, not the username.  */
2403         case 'p':
2404             string_sub (p, "%p", automount_path (lp_servicename (SNUM (conn))));
2405             break;
2406         case '\0':
2407             p++;
2408             break;              /* don't run off the end of the string 
2409                                  */
2410 
2411         default:
2412             p += 2;
2413             break;
2414         }
2415     }
2416 
2417     standard_sub_basic (str);
2418 }
2419 
2420 
2421 
2422 /*******************************************************************
2423 are two IPs on the same subnet?
2424 ********************************************************************/
2425 BOOL
2426 same_net (struct in_addr ip1, struct in_addr ip2, struct in_addr mask)
     /* [previous][next][first][last][top][bottom][index][help]  */
2427 {
2428     uint32 net1, net2, nmask;
2429 
2430     nmask = ntohl (mask.s_addr);
2431     net1 = ntohl (ip1.s_addr);
2432     net2 = ntohl (ip2.s_addr);
2433 
2434     return ((net1 & nmask) == (net2 & nmask));
2435 }
2436 
2437 
2438 /****************************************************************************
2439 a wrapper for gethostbyname() that tries with all lower and all upper case 
2440 if the initial name fails
2441 ****************************************************************************/
2442 struct hostent *
2443 Get_Hostbyname (const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
2444 {
2445     char *name2 = strdup (name);
2446     struct hostent *ret;
2447 
2448     if (!name2)
2449     {
2450         DEBUG (0, ("Memory allocation error in Get_Hostbyname! panic\n"));
2451         exit (0);
2452     }
2453 
2454 
2455     /* 
2456      * This next test is redundent and causes some systems (with
2457      * broken isalnum() calls) problems.
2458      * JRA.
2459      */
2460 
2461 #if 0
2462     if (!isalnum (*name2))
2463     {
2464         free (name2);
2465         return (NULL);
2466     }
2467 #endif /* 0 */
2468 
2469     ret = sys_gethostbyname (name2);
2470     if (ret != NULL)
2471     {
2472         free (name2);
2473         return (ret);
2474     }
2475 
2476     /* try with all lowercase */
2477     strlower (name2);
2478     ret = sys_gethostbyname (name2);
2479     if (ret != NULL)
2480     {
2481         free (name2);
2482         return (ret);
2483     }
2484 
2485     /* try with all uppercase */
2486     strupper (name2);
2487     ret = sys_gethostbyname (name2);
2488     if (ret != NULL)
2489     {
2490         free (name2);
2491         return (ret);
2492     }
2493 
2494     /* nothing works :-( */
2495     free (name2);
2496     return (NULL);
2497 }
2498 
2499 #if 0
2500 /*******************************************************************
2501 turn a uid into a user name
2502 ********************************************************************/
2503 char *
2504 uidtoname (uid_t uid)
     /* [previous][next][first][last][top][bottom][index][help]  */
2505 {
2506     static char name[40];
2507     struct passwd *pass = getpwuid (uid);
2508     if (pass)
2509         return (pass->pw_name);
2510     slprintf (name, sizeof (name) - 1, "%d", (int) uid);
2511     return (name);
2512 }
2513 #endif /* 0 */
2514 
2515 /*******************************************************************
2516 turn a gid into a group name
2517 ********************************************************************/
2518 
2519 char *
2520 gidtoname (gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help]  */
2521 {
2522     static char name[40];
2523     struct group *grp = getgrgid (gid);
2524     if (grp)
2525         return (grp->gr_name);
2526     slprintf (name, sizeof (name) - 1, "%d", (int) gid);
2527     return (name);
2528 }
2529 
2530 #if 0
2531 /*******************************************************************
2532 turn a user name into a uid
2533 ********************************************************************/
2534 uid_t
2535 nametouid (const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
2536 {
2537     struct passwd *pass = getpwnam (name);
2538     if (pass)
2539         return (pass->pw_uid);
2540     return (uid_t) - 1;
2541 }
2542 #endif /* 0 */
2543 /*******************************************************************
2544 something really nasty happened - panic!
2545 ********************************************************************/
2546 void
2547 smb_panic (const char *why)
     /* [previous][next][first][last][top][bottom][index][help]  */
2548 {
2549     const char *cmd = lp_panic_action ();
2550     if (cmd && *cmd)
2551     {
2552         if (system (cmd))
2553         {
2554             DEBUG (0, ("PANIC: cannot run panic handler command \"%s\"\n", cmd));
2555         }
2556     }
2557     DEBUG (0, ("PANIC: %s\n", why));
2558     dbgflush ();
2559     abort ();
2560 }
2561 
2562 #if 0
2563 /*******************************************************************
2564 a readdir wrapper which just returns the file name
2565 ********************************************************************/
2566 char *
2567 readdirname (DIR * p)
     /* [previous][next][first][last][top][bottom][index][help]  */
2568 {
2569     SMB_STRUCT_DIRENT *ptr;
2570     char *dname;
2571 
2572     if (!p)
2573         return (NULL);
2574 
2575     ptr = (SMB_STRUCT_DIRENT *) sys_readdir (p);
2576     if (!ptr)
2577         return (NULL);
2578 
2579     dname = ptr->d_name;
2580 
2581 #ifdef NEXT2
2582     if (telldir (p) < 0)
2583         return (NULL);
2584 #endif
2585 
2586 #ifdef HAVE_BROKEN_READDIR
2587     /* using /usr/ucb/cc is BAD */
2588     dname = dname - 2;
2589 #endif
2590 
2591     {
2592         static pstring buf;
2593         memcpy (buf, dname, NAMLEN (ptr) + 1);
2594         dname = buf;
2595     }
2596 
2597     return (dname);
2598 }
2599 
2600 /*******************************************************************
2601  Utility function used to decide if the last component 
2602  of a path matches a (possibly wildcarded) entry in a namelist.
2603 ********************************************************************/
2604 
2605 BOOL
2606 is_in_path (char *name, name_compare_entry * namelist)
     /* [previous][next][first][last][top][bottom][index][help]  */
2607 {
2608     pstring last_component;
2609     char *p;
2610 
2611     DEBUG (8, ("is_in_path: %s\n", name));
2612 
2613     /* if we have no list it's obviously not in the path */
2614     if ((namelist == NULL) || ((namelist != NULL) && (namelist[0].name == NULL)))
2615     {
2616         DEBUG (8, ("is_in_path: no name list.\n"));
2617         return False;
2618     }
2619 
2620     /* Get the last component of the unix name. */
2621     p = strrchr (name, '/');
2622     strncpy (last_component, p ? ++p : name, sizeof (last_component) - 1);
2623     last_component[sizeof (last_component) - 1] = '\0';
2624 
2625     for (; namelist->name != NULL; namelist++)
2626     {
2627         if (namelist->is_wild)
2628         {
2629             /* 
2630              * Look for a wildcard match. Use the old
2631              * 'unix style' mask match, rather than the
2632              * new NT one.
2633              */
2634             if (unix_mask_match (last_component, namelist->name, case_sensitive, False))
2635             {
2636                 DEBUG (8, ("is_in_path: mask match succeeded\n"));
2637                 return True;
2638             }
2639         }
2640         else
2641         {
2642             if ((case_sensitive && (strcmp (last_component, namelist->name) == 0)) ||
2643                 (!case_sensitive && (StrCaseCmp (last_component, namelist->name) == 0)))
2644             {
2645                 DEBUG (8, ("is_in_path: match succeeded\n"));
2646                 return True;
2647             }
2648         }
2649     }
2650     DEBUG (8, ("is_in_path: match not found\n"));
2651 
2652     return False;
2653 }
2654 
2655 /*******************************************************************
2656  Strip a '/' separated list into an array of 
2657  name_compare_enties structures suitable for 
2658  passing to is_in_path(). We do this for
2659  speed so we can pre-parse all the names in the list 
2660  and don't do it for each call to is_in_path().
2661  namelist is modified here and is assumed to be 
2662  a copy owned by the caller.
2663  We also check if the entry contains a wildcard to
2664  remove a potentially expensive call to mask_match
2665  if possible.
2666 ********************************************************************/
2667 
2668 void
2669 set_namearray (name_compare_entry ** ppname_array, char *namelist)
     /* [previous][next][first][last][top][bottom][index][help]  */
2670 {
2671     char *name_end;
2672     char *nameptr = namelist;
2673     int num_entries = 0;
2674     int i;
2675 
2676     (*ppname_array) = NULL;
2677 
2678     if ((nameptr == NULL) || ((nameptr != NULL) && (*nameptr == '\0')))
2679         return;
2680 
2681     /* We need to make two passes over the string. The
2682        first to count the number of elements, the second
2683        to split it.
2684      */
2685     while (*nameptr)
2686     {
2687         if (*nameptr == '/')
2688         {
2689             /* cope with multiple (useless) /s) */
2690             nameptr++;
2691             continue;
2692         }
2693         /* find the next / */
2694         name_end = strchr (nameptr, '/');
2695 
2696         /* oops - the last check for a / didn't find one. */
2697         if (name_end == NULL)
2698             break;
2699 
2700         /* next segment please */
2701         nameptr = name_end + 1;
2702         num_entries++;
2703     }
2704 
2705     if (num_entries == 0)
2706         return;
2707 
2708     if (((*ppname_array) = (name_compare_entry *) malloc ((num_entries +
2709                                                            1) * sizeof (name_compare_entry))) ==
2710         NULL)
2711     {
2712         DEBUG (0, ("set_namearray: malloc fail\n"));
2713         return;
2714     }
2715 
2716     /* Now copy out the names */
2717     nameptr = namelist;
2718     i = 0;
2719     while (*nameptr)
2720     {
2721         if (*nameptr == '/')
2722         {
2723             /* cope with multiple (useless) /s) */
2724             nameptr++;
2725             continue;
2726         }
2727         /* find the next / */
2728         if ((name_end = strchr (nameptr, '/')) != NULL)
2729         {
2730             *name_end = 0;
2731         }
2732 
2733         /* oops - the last check for a / didn't find one. */
2734         if (name_end == NULL)
2735             break;
2736 
2737         (*ppname_array)[i].is_wild = ((strchr (nameptr, '?') != NULL) ||
2738                                       (strchr (nameptr, '*') != NULL));
2739         if (((*ppname_array)[i].name = strdup (nameptr)) == NULL)
2740         {
2741             DEBUG (0, ("set_namearray: malloc fail (1)\n"));
2742             return;
2743         }
2744 
2745         /* next segment please */
2746         nameptr = name_end + 1;
2747         i++;
2748     }
2749 
2750     (*ppname_array)[i].name = NULL;
2751 
2752     return;
2753 }
2754 
2755 /****************************************************************************
2756 routine to free a namearray.
2757 ****************************************************************************/
2758 
2759 void
2760 free_namearray (name_compare_entry * name_array)
     /* [previous][next][first][last][top][bottom][index][help]  */
2761 {
2762     if (name_array == 0)
2763         return;
2764 
2765     if (name_array->name != NULL)
2766         free (name_array->name);
2767 
2768     free ((char *) name_array);
2769 }
2770 
2771 
2772 /*******************************************************************
2773 is the name specified one of my netbios names
2774 returns true is it is equal, false otherwise
2775 ********************************************************************/
2776 BOOL
2777 is_myname (char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
2778 {
2779     int n;
2780     BOOL ret = False;
2781 
2782     for (n = 0; my_netbios_names[n]; n++)
2783     {
2784         if (strequal (my_netbios_names[n], s))
2785             ret = True;
2786     }
2787     DEBUG (8, ("is_myname(\"%s\") returns %d\n", s, ret));
2788     return (ret);
2789 }
2790 #endif /* 0 */
2791 #if 0                           /* Can be useful one day */
2792 /*******************************************************************
2793 set the horrid remote_arch string based on an enum.
2794 ********************************************************************/
2795 void
2796 set_remote_arch (enum remote_arch_types type)
     /* [previous][next][first][last][top][bottom][index][help]  */
2797 {
2798     ra_type = type;
2799     switch (type)
2800     {
2801     case RA_WFWG:
2802         remote_arch = "WfWg";
2803         return;
2804     case RA_OS2:
2805         remote_arch = "OS2";
2806         return;
2807     case RA_WIN95:
2808         remote_arch = "Win95";
2809         return;
2810     case RA_WINNT:
2811         remote_arch = "WinNT";
2812         return;
2813     case RA_SAMBA:
2814         remote_arch = "Samba";
2815         return;
2816     default:
2817         ra_type = RA_UNKNOWN;
2818         remote_arch = "UNKNOWN";
2819         break;
2820     }
2821 }
2822 
2823 /*******************************************************************
2824  Get the remote_arch type.
2825 ********************************************************************/
2826 enum remote_arch_types
2827 get_remote_arch (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2828 {
2829     return ra_type;
2830 }
2831 #endif /* 0 */
2832 #if 0
2833 /*******************************************************************
2834 align a pointer to a multiple of 2 bytes
2835 ********************************************************************/
2836 char *
2837 align2 (char *q, char *base)
     /* [previous][next][first][last][top][bottom][index][help]  */
2838 {
2839     if ((q - base) & 1)
2840     {
2841         q++;
2842     }
2843     return q;
2844 }
2845 
2846 void
2847 out_ascii (FILE * f, unsigned char *buf, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
2848 {
2849     int i;
2850     for (i = 0; i < len; i++)
2851     {
2852         fprintf (f, "%c", isprint (buf[i]) ? buf[i] : '.');
2853     }
2854 }
2855 
2856 void
2857 out_data (FILE * f, char *buf1, int len, int per_line)
     /* [previous][next][first][last][top][bottom][index][help]  */
2858 {
2859     unsigned char *buf = (unsigned char *) buf1;
2860     int i = 0;
2861     if (len <= 0)
2862     {
2863         return;
2864     }
2865 
2866     fprintf (f, "[%03X] ", i);
2867     for (i = 0; i < len;)
2868     {
2869         fprintf (f, "%02X ", (int) buf[i]);
2870         i++;
2871         if (i % (per_line / 2) == 0)
2872             fprintf (f, " ");
2873         if (i % per_line == 0)
2874         {
2875             out_ascii (f, &buf[i - per_line], per_line / 2);
2876             fprintf (f, " ");
2877             out_ascii (f, &buf[i - per_line / 2], per_line / 2);
2878             fprintf (f, "\n");
2879             if (i < len)
2880                 fprintf (f, "[%03X] ", i);
2881         }
2882     }
2883     if ((i % per_line) != 0)
2884     {
2885         int n;
2886 
2887         n = per_line - (i % per_line);
2888         fprintf (f, " ");
2889         if (n > (per_line / 2))
2890             fprintf (f, " ");
2891         while (n--)
2892         {
2893             fprintf (f, "   ");
2894         }
2895         n = MIN (per_line / 2, i % per_line);
2896         out_ascii (f, &buf[i - (i % per_line)], n);
2897         fprintf (f, " ");
2898         n = (i % per_line) - n;
2899         if (n > 0)
2900             out_ascii (f, &buf[i - n], n);
2901         fprintf (f, "\n");
2902     }
2903 }
2904 #endif /* 0 */
2905 
2906 void
2907 print_asc (int level, unsigned char *buf, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
2908 {
2909     int i;
2910     for (i = 0; i < len; i++)
2911         DEBUG (level, ("%c", isprint (buf[i]) ? buf[i] : '.'));
2912 }
2913 
2914 void
2915 dump_data (int level, char *buf1, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
2916 {
2917     unsigned char *buf = (unsigned char *) buf1;
2918     int i = 0;
2919     if (len <= 0)
2920         return;
2921 
2922     DEBUG (level, ("[%03X] ", i));
2923     for (i = 0; i < len;)
2924     {
2925         DEBUG (level, ("%02X ", (int) buf[i]));
2926         i++;
2927         if (i % 8 == 0)
2928             DEBUG (level, (" "));
2929         if (i % 16 == 0)
2930         {
2931             print_asc (level, &buf[i - 16], 8);
2932             DEBUG (level, (" "));
2933             print_asc (level, &buf[i - 8], 8);
2934             DEBUG (level, ("\n"));
2935             if (i < len)
2936                 DEBUG (level, ("[%03X] ", i));
2937         }
2938     }
2939     if (i % 16)
2940     {
2941         int n;
2942 
2943         n = 16 - (i % 16);
2944         DEBUG (level, (" "));
2945         if (n > 8)
2946             DEBUG (level, (" "));
2947         while (n--)
2948             DEBUG (level, ("   "));
2949 
2950         n = MIN (8, i % 16);
2951         print_asc (level, &buf[i - (i % 16)], n);
2952         DEBUG (level, (" "));
2953         n = (i % 16) - n;
2954         if (n > 0)
2955             print_asc (level, &buf[i - n], n);
2956         DEBUG (level, ("\n"));
2957     }
2958 }
2959 
2960 #if 0
2961 /*****************************************************************************
2962  * Provide a checksum on a string
2963  *
2964  *  Input:  s - the null-terminated character string for which the checksum
2965  *              will be calculated.
2966  *
2967  *  Output: The checksum value calculated for s.
2968  *
2969  * ****************************************************************************
2970  */
2971 int
2972 str_checksum (const char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
2973 {
2974     int res = 0;
2975     int c;
2976     int i = 0;
2977 
2978     while (*s)
2979     {
2980         c = *s;
2981         res ^= (c << (i % 15)) ^ (c >> (15 - (i % 15)));
2982         s++;
2983         i++;
2984     }
2985     return (res);
2986 }                               /* str_checksum */
2987 
2988 
2989 /*****************************************************************
2990 zero a memory area then free it. Used to catch bugs faster
2991 *****************************************************************/
2992 void
2993 zero_free (void *p, size_t size)
     /* [previous][next][first][last][top][bottom][index][help]  */
2994 {
2995     memset (p, 0, size);
2996     free (p);
2997 }
2998 #endif /* 0 */

/* [previous][next][first][last][top][bottom][index][help]  */