text: /*
   * Directory id handling
   *
   * Copyright 2002 Alexandre Julliard for CodeWeavers
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
   */
  
  #include <stdarg.h>
  
  #include "windef.h"
  #include "winbase.h"
  #include "winreg.h"
  #include "winternl.h"
  #include "winerror.h"
  #include "wingdi.h"
  #include "winuser.h"
  #include "winnls.h"
  #include "winspool.h"
  #include "setupapi.h"
  #include "shlobj.h"
  #include "setupapi_private.h"
  #include "wine/debug.h"
  
  WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
  
  #define MAX_SYSTEM_DIRID DIRID_PRINTPROCESSOR
  #define MIN_CSIDL_DIRID 0x4000
  #define MAX_CSIDL_DIRID 0x403f
  
  struct user_dirid
  {
      int    id;
      WCHAR *str;
  };
  
  static int nb_user_dirids;     /* number of user dirids in use */
  static int alloc_user_dirids;  /* number of allocated user dirids */
  static struct user_dirid *user_dirids;
  static const WCHAR *system_dirids[MAX_SYSTEM_DIRID+1];
  static const WCHAR *csidl_dirids[MAX_CSIDL_DIRID-MIN_CSIDL_DIRID+1];
  
  /* retrieve the string for unknown dirids */
  static const WCHAR *get_unknown_dirid(void)
  {
      static WCHAR *unknown_dirid;
      static const WCHAR unknown_str[] = {'\\','u','n','k','n','o','w','n',0};
  
      if (!unknown_dirid)
      {
          UINT len = GetSystemDirectoryW( NULL, 0 ) + lstrlenW(unknown_str);
          if (!(unknown_dirid = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
          GetSystemDirectoryW( unknown_dirid, len );
          lstrcatW( unknown_dirid, unknown_str );
      }
      return unknown_dirid;
  }
  
  static const WCHAR *get_csidl_dir(DWORD csidl);
  
  /* create the string for a system dirid */
  static const WCHAR *create_system_dirid( int dirid )
  {
      static const WCHAR Null[]    = {0};
      static const WCHAR C_Root[]  = {'C',':','\\',0};
      static const WCHAR Drivers[] = {'\\','d','r','i','v','e','r','s',0};
      static const WCHAR Inf[]     = {'\\','i','n','f',0};
      static const WCHAR Help[]    = {'\\','h','e','l','p',0};
      static const WCHAR Fonts[]   = {'\\','f','o','n','t','s',0};
      static const WCHAR Viewers[] = {'\\','v','i','e','w','e','r','s',0};
      static const WCHAR System[]  = {'\\','s','y','s','t','e','m',0};
      static const WCHAR Spool[]   = {'\\','s','p','o','o','l',0};
      static const WCHAR UserProfile[] = {'U','S','E','R','P','R','O','F','I','L','E',0};
  
      WCHAR buffer[MAX_PATH+32], *str;
      int len;
      DWORD needed;
  
      switch(dirid)
      {
      case DIRID_NULL:
          return Null;
      case DIRID_WINDOWS:
          GetWindowsDirectoryW( buffer, MAX_PATH );
          break;
      case DIRID_SYSTEM:
          GetSystemDirectoryW( buffer, MAX_PATH );
          break;
      case DIRID_DRIVERS:
          GetSystemDirectoryW( buffer, MAX_PATH );
          lstrcatW( buffer, Drivers );
          break;
      case DIRID_INF:
          GetWindowsDirectoryW( buffer, MAX_PATH );
          lstrcatW( buffer, Inf );
          break;
      case DIRID_HELP:
          GetWindowsDirectoryW( buffer, MAX_PATH );
          lstrcatW( buffer, Help );
          break;
      case DIRID_FONTS:
          GetWindowsDirectoryW( buffer, MAX_PATH );
          lstrcatW( buffer, Fonts );
          break;
      case DIRID_VIEWERS:
          GetSystemDirectoryW( buffer, MAX_PATH );
          lstrcatW( buffer, Viewers );
          break;
      case DIRID_APPS:
          return C_Root;  /* FIXME */
      case DIRID_SHARED:
          GetWindowsDirectoryW( buffer, MAX_PATH );
          break;
      case DIRID_BOOT:
          return C_Root;  /* FIXME */
      case DIRID_SYSTEM16:
          GetWindowsDirectoryW( buffer, MAX_PATH );
          lstrcatW( buffer, System );
          break;
      case DIRID_SPOOL:
      case DIRID_SPOOLDRIVERS:  /* FIXME */
          GetWindowsDirectoryW( buffer, MAX_PATH );
          lstrcatW( buffer, Spool );
          break;
      case DIRID_USERPROFILE:
          if (GetEnvironmentVariableW( UserProfile, buffer, MAX_PATH )) break;
          return get_csidl_dir(CSIDL_PROFILE);
      case DIRID_LOADER:
          return C_Root;  /* FIXME */
      case DIRID_PRINTPROCESSOR:
          if (!GetPrintProcessorDirectoryW(NULL, NULL, 1, (LPBYTE)buffer, sizeof(buffer), &needed))
          {
              WARN( "cannot retrieve print processor directory\n" );
              return get_unknown_dirid();
          }
          break;
      case DIRID_COLOR:  /* FIXME */
      default:
          FIXME( "unknown dirid %d\n", dirid );
          return get_unknown_dirid();
      }
      len = (lstrlenW(buffer) + 1) * sizeof(WCHAR);
      if ((str = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( str, buffer, len );
      return str;
  }
  
  static const WCHAR *get_csidl_dir( DWORD csidl )
  {
      WCHAR buffer[MAX_PATH], *str;
      int len;
  
      if (!SHGetSpecialFolderPathW( NULL, buffer, csidl, TRUE ))
      {
          FIXME( "CSIDL %x not found\n", csidl );
          return get_unknown_dirid();
      }
      len = (lstrlenW(buffer) + 1) * sizeof(WCHAR);
      if ((str = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( str, buffer, len );
      return str;
  }
  
  /* retrieve the string corresponding to a dirid, or NULL if none */
  const WCHAR *DIRID_get_string( int dirid )
  {
      int i;
  
      if (dirid == DIRID_ABSOLUTE || dirid == DIRID_ABSOLUTE_16BIT) dirid = DIRID_NULL;
  
      if (dirid >= DIRID_USER)
      {
          for (i = 0; i < nb_user_dirids; i++)
              if (user_dirids[i].id == dirid) return user_dirids[i].str;
          WARN("user id %d not found\n", dirid );
          return NULL;
      }
      else if (dirid >= MIN_CSIDL_DIRID)
      {
          if (dirid > MAX_CSIDL_DIRID) return get_unknown_dirid();
          dirid -= MIN_CSIDL_DIRID;
          if (!csidl_dirids[dirid]) csidl_dirids[dirid] = get_csidl_dir( dirid );
          return csidl_dirids[dirid];
      }
      else
      {
          if (dirid > MAX_SYSTEM_DIRID) return get_unknown_dirid();
          if (!system_dirids[dirid]) system_dirids[dirid] = create_system_dirid( dirid );
          return system_dirids[dirid];
      }
  }
  
  /* store a user dirid string */
  static BOOL store_user_dirid( HINF hinf, int id, WCHAR *str )
  {
      int i;
  
      for (i = 0; i < nb_user_dirids; i++) if (user_dirids[i].id == id) break;
  
      if (i < nb_user_dirids) HeapFree( GetProcessHeap(), 0, user_dirids[i].str );
      else
      {
          if (nb_user_dirids >= alloc_user_dirids)
          {
              int new_size = max( 32, alloc_user_dirids * 2 );
  
  	    struct user_dirid *new;
  
  	    if (user_dirids)
                  new = HeapReAlloc( GetProcessHeap(), 0, user_dirids,
                                                    new_size * sizeof(*new) );
  	    else
                  new = HeapAlloc( GetProcessHeap(), 0, 
                                                    new_size * sizeof(*new) );
  
              if (!new) return FALSE;
              user_dirids = new;
              alloc_user_dirids = new_size;
          }
          nb_user_dirids++;
      }
      user_dirids[i].id  = id;
      user_dirids[i].str = str;
      TRACE("id %d -> %s\n", id, debugstr_w(str) );
      return TRUE;
  }
  
  
  /***********************************************************************
   *		SetupSetDirectoryIdA    (SETUPAPI.@)
   */
  BOOL WINAPI SetupSetDirectoryIdA( HINF hinf, DWORD id, PCSTR dir )
  {
      UNICODE_STRING dirW;
      int i;
  
      if (!id)  /* clear everything */
      {
          for (i = 0; i < nb_user_dirids; i++) HeapFree( GetProcessHeap(), 0, user_dirids[i].str );
          nb_user_dirids = 0;
          return TRUE;
      }
      if (id < DIRID_USER)
      {
          SetLastError( ERROR_INVALID_PARAMETER );
          return FALSE;
      }
  
      /* duplicate the string */
      if (!RtlCreateUnicodeStringFromAsciiz( &dirW, dir ))
      {
          SetLastError( ERROR_NOT_ENOUGH_MEMORY );
          return FALSE;
      }
      return store_user_dirid( hinf, id, dirW.Buffer );
  }
  
  
  /***********************************************************************
   *		SetupSetDirectoryIdW    (SETUPAPI.@)
   */
  BOOL WINAPI SetupSetDirectoryIdW( HINF hinf, DWORD id, PCWSTR dir )
  {
      int i, len;
      WCHAR *str;
  
      if (!id)  /* clear everything */
      {
          for (i = 0; i < nb_user_dirids; i++) HeapFree( GetProcessHeap(), 0, user_dirids[i].str );
          nb_user_dirids = 0;
          return TRUE;
      }
      if (id < DIRID_USER)
      {
          SetLastError( ERROR_INVALID_PARAMETER );
          return FALSE;
      }
  
      /* duplicate the string */
      len = (lstrlenW(dir)+1) * sizeof(WCHAR);
      if (!(str = HeapAlloc( GetProcessHeap(), 0, len ))) return FALSE;
      memcpy( str, dir, len );
      return store_user_dirid( hinf, id, str );
  }
  