#include "wxinc.h"
#include "wx/print.h"
#include "wx/printdlg.h"

#include "xvt.h"

#ifdef WIN32
#include "wx/dcps.h"
#include "wx/msw/printdlg.h"
#include "oswin32.h"
#else
#include "wx/setup.h"
#include "wx/dcps.h"
#include "oslinux.h"
#include "incstr.h"
#endif

#include "xvtpdf.h"
#include "xvtwin.h"

#pragma pack(4)

struct TPRINT_RCD : public PRINT_RCD
{
#ifdef WIN32
	unsigned char m_data[16*1024];
	unsigned int m_size;   // Dimensione della struct DEVMODE

	void SetData(void * data, unsigned int nSize);
#else
	wxPrintNativeDataBase * m_data;
	unsigned int m_size;   // Dimensione di wxPostScriptPrintNativeData
	
	void GetData(wxPrintNativeDataBase * data) const;
	void SetData(void * data);


#endif

  unsigned int GetSize() const { return m_size; } 

  TPRINT_RCD();
	~TPRINT_RCD();
};

#pragma pack()


#ifdef WIN32
void TPRINT_RCD::SetData(void* data, unsigned int nSize)
{
  if (nSize <= sizeof(m_data))
	{
    memcpy(m_data, data, nSize);
	  m_size = nSize;
	}
  else
    xvt_dm_post_error("Printer info exceeds 16K");
}

TPRINT_RCD::TPRINT_RCD() : m_size(0)
{
	pr = NULL;
	memset(m_data, 0, sizeof(m_data));
}

TPRINT_RCD::~TPRINT_RCD()
{
	memset(m_data, 0, sizeof(m_data));
	m_size = 0;

}
#else
void TPRINT_RCD::GetData(wxPrintNativeDataBase * data) const 
{
  memcpy(data, m_data, GetSize());
}

void TPRINT_RCD::SetData(void* data)
{
  memcpy(m_data, data, GetSize());
}

wxNativePrintFactory __factory;

TPRINT_RCD::TPRINT_RCD()
{
  m_data = __factory.CreatePrintNativeData();
	m_size = sizeof(*m_data);
}

TPRINT_RCD::~TPRINT_RCD()
{
	delete m_data;
}
#endif

///////////////////////////////////////////////////////////
// TwxPrintOut
///////////////////////////////////////////////////////////

class TwxPrintOut : public wxPrintout
{
protected:
  const TPRINT_RCD* m_prcd;
  bool m_bBadDriver;

  virtual bool HasPage(int pageNum);
	virtual bool OnPrintPage(int pageNum);
  void ResetDC();

  wxDC* CreateDC(const TPRINT_RCD* prcd, const char* title);


public:
  void SetBadDriver(bool bd) { m_bBadDriver = bd; }
  bool HasBadDriver() const { return m_bBadDriver; }
   
  wxString PrinterName() const;
  bool IsPDF() const { return !HasBadDriver() && xvt_print_is_pdf((PRINT_RCD *)m_prcd) != 0; }
   
  void InitDC(const TPRINT_RCD* prcd, const char* title);
	TwxPrintOut(const TPRINT_RCD* prcd = NULL);

	virtual ~TwxPrintOut() {}
};

wxString TwxPrintOut::PrinterName() const
{
  char strName[MAX_PATH] = "";
  xvt_print_get_name(m_prcd, strName, sizeof(strName));
  return strName;
}

bool TwxPrintOut::HasPage(int pageNum)
{	return true; }

bool TwxPrintOut::OnPrintPage(int pageNum)
{	return false; }

void TwxPrintOut::ResetDC()
{
	wxDC* dc = GetDC();
	if (dc != NULL)
  {
		delete dc;
    SetDC(NULL);
  }
}


static void RCD2data(const TPRINT_RCD* prcd, wxPrintData& data)
{
#ifdef WIN32
  wxWindowsPrintNativeData ndb;
  ndb.SetDevMode(OsWin32_ConvertToNativePrinterInfo((void*)prcd->m_data, prcd->m_size));
  ndb.TransferTo(data); 
#else
 prcd->GetData(data.GetNativeData());
 data.ConvertFromNative();
#endif
}

static void data2RCD(const wxPrintData& data, TPRINT_RCD* prcd)
{
#ifdef WIN32
  wxWindowsPrintNativeData* pNative = (wxWindowsPrintNativeData*)data.GetNativeData();
  unsigned int nSize = 0;
  void* ptr = OsWin32_ConvertFromNativePrinterInfo(pNative->GetDevMode(), nSize);
  prcd->SetData(ptr, nSize);
  delete ptr;
#else
  ((wxPrintData&)data).ConvertToNative();
  prcd->SetData(data.GetNativeData());
#endif
}

wxDC* TwxPrintOut::CreateDC(const TPRINT_RCD* prcd, const char* title)
{
  m_prcd = prcd;
  wxDC* dc = NULL;

	if (m_prcd == NULL)
	{
    wxPrinter printer;
#ifdef WIN32
    dc = new wxPrinterDC(printer.GetPrintDialogData().GetPrintData());
#else
    dc = new wxPostScriptDC(printer.GetPrintDialogData().GetPrintData());
#endif    
	}
	else 
	{
		wxPrintData data;
    
		RCD2data(prcd, data);
    
		const wxString strName = PrinterName();

    data.SetPrinterName(strName);
    
		const bool ispdf = IsPDF();

    if (ispdf)
      dc = new TwxPDFDC(data, title);
    else
#ifdef WIN32
      dc = new wxPrinterDC(data);
#else
      dc = new wxPostScriptDC(data);
#endif
	}
  wxSize s = dc->GetPPI();
  if (s.x > 0)
    SetPPIPrinter(s.x, s.y);


  s = dc->GetSize();
  if (s.x > 0)
    SetPageSizePixels(s.x, s.y);

  SetDC(dc);
  
  wxWindow* pAbort = wxPrinterBase::sm_abortWindow;
  if (pAbort != NULL)
  {
    wxWindow* pStatic = pAbort->FindWindow(wxID_STATIC);
    if (pStatic != NULL)
    {
      wxString strPrompt = wxT("Stampa su ");
      if (IsPDF())
        strPrompt += wxT("PDF");
      else
        strPrompt += PrinterName();
      pStatic->SetLabel(strPrompt);
    }
  }
  return dc;
}

void TwxPrintOut::InitDC(const TPRINT_RCD* prcd, const char* title)
{
  ResetDC();
  wxDC* dc = CreateDC(prcd, title);
 
	wxSize s = dc->GetPPI();
	if (s.x >0)
    SetPPIPrinter(s.x, s.y);

  s = dc->GetSize();
  if (s.x > 0)
    SetPageSizePixels(s.x, s.y);

  SetDC(dc);
}

TwxPrintOut::TwxPrintOut(const TPRINT_RCD* prcd)
           : wxPrintout(_GetAppTitle()), m_bBadDriver(false)
{
	InitDC(prcd, _GetAppTitle());
}

///////////////////////////////////////////////////////////
// TwxPrintOutCache
///////////////////////////////////////////////////////////

class TwxPrintOutCache
{
  unsigned long m_signature;
  TwxPrintOut* m_po;
  bool m_bLocked;

protected:
  unsigned long Signature(const TPRINT_RCD* prcd) const;

public:
  TwxPrintOut& Get(const TPRINT_RCD* prcd);
  void Reset();

  void Lock() { m_bLocked = true; }
  void Unlock() { m_bLocked = false; }

  bool Ok() const { return m_po != NULL; }
  bool Locked() const { return m_bLocked; }
  bool Printing() const { return Ok() && Locked(); }

  TwxPrintOutCache();
  ~TwxPrintOutCache();
} m_PrintoutCache;

unsigned long TwxPrintOutCache::Signature(const TPRINT_RCD* prcd) const
{
  unsigned long h = 0;
  if (prcd != NULL)
  {
    const unsigned char* data = (const unsigned char*)prcd;
    for (size_t c = 0; c < prcd->GetSize(); c++)
    {
      h = (h << 2) + data[c];
      const unsigned long i = h & 0xC0000000;
      if (i) h = (h ^ (i >> 12)) & 0x3FFFFFFF;
    }
  }
  return h;
}

void TwxPrintOutCache::Reset()
{
  wxASSERT(!Locked());
  if (m_po != NULL)
  {
    delete m_po;
    m_po = NULL;
  }
}

TwxPrintOut& TwxPrintOutCache::Get(const TPRINT_RCD* prcd)
{
  if (!Locked())
  {
    if (prcd == NULL)
    {
      if (m_po == NULL)
        m_po = new TwxPrintOut;
    }
    else
    {
      unsigned long signature = Signature(prcd);
      if (m_po != NULL && m_signature == signature)
        return *m_po;
      Reset();
      m_po = new TwxPrintOut(prcd);
      m_signature = signature;
    }
  }
  wxASSERT(m_po != NULL);
  
  return *m_po;
}

TwxPrintOutCache::TwxPrintOutCache() : m_signature(0), m_po(NULL) 
{ }

TwxPrintOutCache::~TwxPrintOutCache() 
{ 
  // Reset();   // Essendo un oggetto statico la delete m_po non funziona!
}

///////////////////////////////////////////////////////////
// TPrintDC
///////////////////////////////////////////////////////////

// Flag di reset ad inizio pagina
bool TPrintDC::_page_start = false;

void TPrintDC::SetPageStart() 
{ _page_start = true; }

wxDC& TPrintDC::GetDC(bool)
{
	_dc = m_PrintoutCache.Get(NULL).GetDC();         // Forza display context corrente
  if (_page_start)	
  {
    _dirty = -1;
    _page_start = false;
  }
	return TDC::GetDC(false);
}

void TPrintDC::KillDC()
{
  _dc = NULL;  // _dc is owned by wxPrintout
}

TPrintDC::TPrintDC(wxWindow* owner) : TDC(owner)
{ 
  _page_start = false;
}

TPrintDC::~TPrintDC()
{
	KillDC();
}

///////////////////////////////////////////////////////////
// Printing management :-(((((
///////////////////////////////////////////////////////////

BOOLEAN xvt_app_escape(int esc_code, PRINT_RCD* rcd, long* ph, long* pw, long* pvr, long* phr)
{
  switch (esc_code)
	{
	case XVT_ESC_GET_PRINTER_INFO:
    if (ph)	*ph = *pw = 0;
    if (pvr) *pvr = *phr = 0;
    if (rcd == NULL || xvt_print_is_valid(rcd))
		{
      const TwxPrintOut& po = m_PrintoutCache.Get((TPRINT_RCD*)rcd);
	    int w, h;
      if (ph)
      {
			  po.GetPageSizePixels(&w, &h);
			  *pw = w; *ph = h;
      }
      if (pvr)
      {
			  po.GetPPIPrinter(&w, &h);
			  *phr = w; *pvr = h;
      }
  		return TRUE;
		}
		break;
	case XVT_ESC_SET_PRINTER_INFO:
		if (xvt_print_is_valid(rcd) && ph != NULL && pw != NULL)
		{
  		wxPrintData data;

      TPRINT_RCD* prcd = (TPRINT_RCD*)rcd;
#ifdef WIN32      

      RCD2data(prcd, data);

      data.SetOrientation(*ph > *pw ? 1 : 2);
      data.ConvertToNative();

      data2RCD(data, prcd);

#else
      RCD2data(prcd, data);
      data.SetOrientation(*ph > *pw ? 1 : 2);
      data2RCD(data, prcd);
#endif
      
      return true;
    }
    break;

  default:
		break;
	}
	return FALSE;
}

BOOLEAN xvt_dm_post_page_setup(PRINT_RCD* precp)
{
	wxPageSetupDialog dlg((wxWindow*)TASK_WIN);
  TPRINT_RCD* rcd = (TPRINT_RCD*)precp;

  wxPageSetupData& pdd = dlg.GetPageSetupData();
  wxPrintData& data = pdd.GetPrintData();

  RCD2data(rcd, data);

  pdd.EnableMargins(false);

  const BOOLEAN ok = dlg.ShowModal() == wxID_OK;
	if (ok)
	{
    data2RCD(data, rcd);
    m_PrintoutCache.Reset();
	}

	return ok;
}

long xvt_fmap_get_family_sizes(PRINT_RCD *precp, char *family, long *size_array, BOOLEAN *scalable, long max_sizes)
{
  long size = 0;
	*scalable = FALSE;

#ifdef WIN32
  if (xvt_print_is_valid(precp))
	{
  	const TwxPrintOut& po = m_PrintoutCache.Get((TPRINT_RCD*)precp);
    if (!po.HasBadDriver())
		  size = OsWin32_EnumerateSizes(po.GetDC()->GetHDC(), family, size_array, scalable, max_sizes);
	}
  else
  {
    size = OsWin32_EnumerateSizes(NULL, family, size_array, scalable, max_sizes);
  }
#else
  size = OsLinux_EnumerateSizes(family, size_array, scalable, max_sizes);
#endif

	return size;
}

long xvt_fmap_get_families(PRINT_RCD *precp, char **family_array, long max_families)
{
	long size = 0;
 	family_array[0] = NULL;

#ifdef WIN32
  if (xvt_print_is_valid(precp))
  {
  	TwxPrintOut& po = m_PrintoutCache.Get((TPRINT_RCD*)precp);
	  size = OsWin32_EnumerateFamilies(po.GetDC()->GetHDC(), family_array, max_families);
    if (size == 0)
      po.SetBadDriver(true);
  }
  else
  {
    wxFrame* tw = (wxFrame*)TASK_WIN;
    wxClientDC dc(tw);
	  size = OsWin32_EnumerateFamilies(dc.GetHDC(), family_array, max_families);
  }
#else
  size = OsLinux_EnumerateFamilies(family_array, max_families);
#endif
	
  return size;
}

void xvt_print_close(void)
{
	// Nothing to do ?
}

BOOLEAN xvt_print_close_page(PRINT_RCD* precp)
{
  BOOLEAN ok = m_PrintoutCache.Printing();
  if (ok)
  {
    const TwxPrintOut& po = m_PrintoutCache.Get(NULL);
    wxDC* dc = po.GetDC();
	  dc->EndPage();
#ifdef WIN32
    TPRINT_RCD* prcd = (TPRINT_RCD*)precp;
    if (OsWin32_IsGenericTextOnly(prcd->m_data))
      OsWin32_SpoolNewLine((unsigned int)dc->GetHDC());
#endif
  }
  return ok;
}

PRINT_RCD* xvt_print_create(int *sizep)
{
	return xvt_print_create_by_name(sizep, NULL);
}

// Impone il nome ad un PRINT_RCD.
// Utilizzato per la definiz. della stamp. virtuale PDF predefinita
int xvt_print_set_name(PRINT_RCD* precp, const char* name)
{
  if (precp == NULL)
    return 0;

  if (!name || !*name)
    return 0;

  wxString n = name;

#ifdef WIN32
  strncpy(((char*)precp) + 4, name, 32);
#else
	TPRINT_RCD* rcd = (TPRINT_RCD*)precp;
	wxPrintData data;
	
  rcd->GetData(data);
  data.SetPrinterName(n);
  rcd->SetData(data);
#endif

  return n.Length();
}

// Nuova funzione inventata da Aga
PRINT_RCD* xvt_print_create_by_name(int* sizep, const char* name)
{
	TPRINT_RCD* pr = NULL;
  *sizep = 0;
  const bool ispdf = (name && *name) && (xvt_str_compare_ignoring_case(name, XVT_PDF_PRINTER_NAME) == 0);

  if (ispdf)
    name = NULL;

#ifdef WIN32
	void* data = OsWin32_GetPrinterInfo(*sizep, name);
	if (data != NULL)
	{
  	pr = new TPRINT_RCD;
		pr->SetData(data, *sizep);
		*sizep += 4;  // Spazio per puntatore iniziale
		delete data;
	}
#else
  wxPrintData data;

  data.SetPrinterName(name == NULL ? "" : name);
	pr = new TPRINT_RCD;
 	data2RCD(data, pr);
	*sizep = pr->GetSize();
#endif

  if (ispdf)
    xvt_print_set_name(pr, XVT_PDF_PRINTER_NAME);
	
  return pr;
}

WINDOW xvt_print_create_win(PRINT_RCD* precp, const char* title)


{
  TPRINT_RCD* rcd = (TPRINT_RCD*)precp;

  if (m_PrintoutCache.Printing())
  {
    TwxPrintOut& po = m_PrintoutCache.Get(NULL);
	  po.InitDC(rcd, title);
    po.OnBeginPrinting();
    po.OnBeginDocument(1, 32000);
  }
  else
  {
    TwxPrintOut& po = m_PrintoutCache.Get(rcd);
	  po.InitDC(rcd, title);
  }
  return PRINTER_WIN;
}

void xvt_print_destroy(PRINT_RCD* precp)
{
	delete precp;
}

RCT* xvt_print_get_next_band(void)
{
	static bool yes = false;
	static RCT rct;
  yes = !yes;

	if (!yes)
		return NULL;

  const TwxPrintOut& po = m_PrintoutCache.Get(NULL);
	int w, h;
	po.GetPageSizePixels(&w, &h);
	rct.left = rct.top = 0;
	rct.right = w;
	rct.bottom = h;

	return &rct;
}

BOOLEAN xvt_print_is_pdf(PRINT_RCD* precp)
{
  char strName[MAX_PATH];  
  xvt_print_get_name(precp, strName, sizeof(strName));
  return (xvt_str_compare_ignoring_case(strName, XVT_PDF_PRINTER_NAME) == 0);
}

BOOLEAN xvt_print_is_valid(const PRINT_RCD* precp)
{
  BOOLEAN ok = precp != NULL && precp->pr == NULL;
	if (ok)
	{
		TPRINT_RCD* rcd = (TPRINT_RCD*)precp;

#ifdef WIN32
		ok = OsWin32_CheckPrinterInfo(rcd->m_data, rcd->m_size);
#else
		wxPrintData data;
    RCD2data(rcd, data);
		ok = data.Ok();
#endif
	}
	return ok;
}

int xvt_print_get_name(const PRINT_RCD* precp, char* name, int sz_s)
{
  if (!xvt_print_is_valid(precp))
    return 0;

#ifdef WIN32
  wxString n = ((const char*)precp) + 4;
  if (n.Length() >= 30)
  {
    SLIST plist = xvt_print_list_devices();
    for (SLIST_ELT pitem = xvt_slist_get_first(plist);
       pitem != NULL; pitem = xvt_slist_get_next(plist, pitem)) 
    {
      const wxString pname = xvt_slist_get(plist, pitem, NULL);
      if (pname.StartsWith(n))
      {
        n = pname;
        break;
      }
    }
    xvt_slist_destroy(plist);
  }
#else
	TPRINT_RCD* rcd = (TPRINT_RCD*)precp;
	wxPrintData data;
	
  RCD2data(rcd, data);
  wxString n = data.GetPrinterName();
#endif
  if (name != NULL && sz_s > 0)
  {
    strncpy(name, n, sz_s);
    name[sz_s-1] = '\0';
  }
  return n.Length();
}

BOOLEAN xvt_print_open(void)
{
	return m_PrintoutCache.Ok();
}

///////////////////////////////////////////////////////////

void CreateAbortWindow()
{
  wxFrame* parent = (wxFrame*)TASK_WIN;
  wxString strCaption = wxT("Stampa");
  wxString strPrompt = strCaption + wxT(" in corso...");
  wxPrintAbortDialog* win = new wxPrintAbortDialog(parent, strCaption, 
                                wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE);

  wxBoxSizer *button_sizer = new wxBoxSizer( wxVERTICAL );

  button_sizer->Add(new wxStaticText( win, wxID_STATIC, strPrompt, wxDefaultPosition, wxSize(320,-1)), 0, wxALL, 10 );
  button_sizer->Add( new wxButton( win, wxID_CANCEL, wxT("Annulla") ), 0, wxALL | wxALIGN_CENTER, 10 );

  win->SetAutoLayout(true);
  win->SetSizer(button_sizer);

  button_sizer->Fit(win);
  button_sizer->SetSizeHints(win);

  win->Show();
  win->Update();

  wxPrinterBase::sm_abortWindow = win;
}

void DestroyAbortWindow()
{
  if (wxPrinterBase::sm_abortWindow != NULL)
  {
    wxPrinterBase::sm_abortWindow->Hide();
    delete wxPrinterBase::sm_abortWindow;
    wxPrinterBase::sm_abortWindow = NULL;
  }
}

///////////////////////////////////////////////////////////

BOOLEAN xvt_print_start_thread(BOOLEAN(*print_fcn)(long), long data)
{
  const wxString strDir = ::wxGetCwd(); // Memorizzo la directory corrente (Acrobat la cambia)

  wxBeginBusyCursor();

  m_PrintoutCache.Reset(); // Forza nuovo contesto di stampa
  TwxPrintOut& po = m_PrintoutCache.Get(NULL);
  m_PrintoutCache.Lock();
  wxEndBusyCursor();

  CreateAbortWindow();
  
	const BOOLEAN aborted = print_fcn(data);
  DestroyAbortWindow();

  po.OnEndDocument();
  po.OnEndPrinting();

  m_PrintoutCache.Unlock();
  m_PrintoutCache.Reset();

  ::wxSetWorkingDirectory(strDir);    // Ripristino la directory corrente (Acrobat l'ha cambiata)

  return aborted;
}

BOOLEAN xvt_print_suspend_thread()
{
  BOOLEAN ok = m_PrintoutCache.Printing();
  if (ok)
  {
    TwxPrintOut& po = m_PrintoutCache.Get(NULL); // Sembra generare una pagina vuota indesiderata
    po.OnEndDocument(); // Sembra generare una pagina vuota indesiderata
    po.OnEndPrinting();
  }
  return ok;
}

BOOLEAN xvt_print_restart_thread()
{
	TwxPrintOut& po = m_PrintoutCache.Get(NULL);
  po.OnBeginPrinting();
  po.OnBeginDocument(1, 32000);

  return TRUE;
}


BOOLEAN xvt_print_open_page(PRINT_RCD* precp)
{
  BOOLEAN ok = m_PrintoutCache.Printing();
	if (ok)
	{
    if (wxPrinterBase::sm_abortIt)
      ok = FALSE;
    else
    {
      TwxPrintOut& po = m_PrintoutCache.Get(NULL);
		  po.GetDC()->StartPage();
      TPrintDC::SetPageStart();   // Flag per azzeramento dati DC
    }
	}
	return ok;
}

///////////////////////////////////////////////////////////
// Added by XVAGA
///////////////////////////////////////////////////////////

#ifdef LINUX
static const char * cups_file = "/etc/cups/printers.conf";
static const char * cups_local_file = "./printers.conf";
static const char * prcap_local_file = "./printcap";

static bool is_cups()
{
  static int printer_system = -1;

  if (printer_system < 0)
    printer_system = xvt_fsys_file_exists(cups_file) ? 1 : 2;
  
  return printer_system == 1;
}
#endif

SLIST xvt_print_list_devices()
{
  SLIST list = xvt_slist_create();
#ifdef WIN32
/* Vecchio modo oblsoleto
  char buf[4096]; memset(buf, 0, sizeof(buf));
  GetProfileString("devices", NULL, "", buf, sizeof(buf));
  int start = 0;
  for (int i = 0; i < sizeof(buf); i++) if (buf[i] == '\0')
  {
    const char* pname = buf+start;
    if (*pname == '\0')
      break;
    xvt_slist_add_at_elt(list, NULL, pname, NULL);
    start = i+1;
  } */

  const DWORD dwFlags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS;
  const int level = xvt_sys_get_os_version() >= XVT_WS_WIN_NT ? 4 : 5;
  DWORD dwSize = 0, dwPrinters = 0;
  ::EnumPrinters (dwFlags, NULL, level, NULL, 0, &dwSize, &dwPrinters); 
  if (dwSize > 0)
  {
    BYTE* pBuffer = new BYTE[dwSize];
    ::EnumPrinters (dwFlags, NULL, level, pBuffer, dwSize, &dwSize, &dwPrinters); 
    if (level == 4)
    {
      const PRINTER_INFO_4* pPrnInfo = (const PRINTER_INFO_4*)pBuffer;
      for (UINT i=0; i < dwPrinters; i++) 
      {
        xvt_slist_add_at_elt(list, NULL, pPrnInfo->pPrinterName, NULL);
        pPrnInfo++;
      }
    }
    else
    {
      const PRINTER_INFO_5* pPrnInfo = (const PRINTER_INFO_5*)pBuffer;
      for (UINT i=0; i < dwPrinters; i++) 
      {
        xvt_slist_add_at_elt(list, NULL, pPrnInfo->pPrinterName, NULL);
        pPrnInfo++;
      }
    }
    delete[] pBuffer;
  }
#else
  if (is_cups())
  {
    ifstream p(cups_local_file); 
    char line[4096];
    const char * str_to_find = "Printer";
    
    while (p.getline(line, sizeof(line)))
    {
      char * s;

      if (line[0] == '<' && line[1] != '/' &&
          (s = strstr(line, str_to_find)) != NULL)
      {
        s += strlen(str_to_find);

        while (isspace(*s))
          s++;

        if (*s)
        {
          char * l = s + strlen(s) - 1;

          while (*l == '>' || isspace(*l))
              l--;
          *(l + 1) = '\0';
          xvt_slist_add_at_elt(list, NULL, s, 0L);
        }
      }
    }
  }
  else
  {
    ifstream p(prcap_local_file); // vedere
    char line[4096];

    while (p.getline(line, sizeof(line)))
    {
      if (line[0] != '#')
      {
        const int len = strlen(line);
        int i;

        for (i = 0; i < len; i++)
        {
          const char c = line[i];

          if (!(isalpha(c) || isdigit(c) || isblank(c)))
            break;
        }
        line[i] = '\0';
        xvt_slist_add_at_elt(list, NULL, line, 0L);
      }
    }
  }
#endif
  return list;
}

BOOLEAN xvt_print_set_default_device(const char* name)
{
  BOOLEAN ok = name && *name > ' ';
#ifdef WIN32
  if (ok)
  {
    wxString pdev(name);
    if (pdev.Find(',') < 0)
    {
      char szDevice[256];
      ::GetProfileString ("devices", pdev, "", szDevice, sizeof(szDevice));
      pdev << ',' << szDevice;
    }
    ok = ::WriteProfileString("windows", "device", pdev) != 0;
  }
#endif
  return ok;
}

BOOLEAN xvt_print_get_default_device(char* name, int namesize)
{
  bool ok = FALSE;
#ifdef WIN32
  ok = ::GetProfileString ("windows", "device", ",,,", name, namesize) != 0;
#else
  *name = '\0';
  if (is_cups())
  {
    ifstream p(cups_local_file); 
    char line[4096];
    const char * str_to_find = "<DefaultPrinter";

    while (p.getline(line, sizeof(line)))
    {
      char * s = strstr(line, str_to_find) ;

      if (s != NULL)
      {
        s += strlen(str_to_find);

        while (isspace(*s))
	        s++;

        if (*s)
        {
          char * l = s + strlen(s) - 1;

          while (*l == '>' || isspace(*l))
            l--;
          *(l + 1) = '\0';
          strcpy(name, s);
        }
        ok = TRUE;
      }
    }
  }
#endif

  return ok;
}

///////////////////////////////////////////////////////////
// Gestione files di configurazione
///////////////////////////////////////////////////////////

int xvt_fsys_get_campo_stp_value(const char* name, char* value, int valsize)
{
  BOOLEAN bFound = FALSE;
#ifdef WIN32
  const char* const stpfile = "c:/campo.stp";
  int p;
  DIRECTORY dir; 
  char exedir[_MAX_PATH], path[_MAX_PATH];
  xvt_fsys_get_default_dir(&dir);
  xvt_fsys_convert_dir_to_str(&dir, exedir, sizeof(exedir));

  for (p = 1; ; p++)
  {
    char para[4]; sprintf(para, "%d", p);
    int len = xvt_sys_get_profile_string(stpfile, para, "Program", "", path, sizeof(path));
    if (len <= 0)
      break;
    if (path[len-1] == '\\' || path[len-1] == '/')
    {
      len--;
      path[len] = '\0';
    }
    if (xvt_str_compare_ignoring_case(path, exedir) == 0)
    {
      xvt_sys_get_profile_string(stpfile, para, name, "", value, valsize);
      bFound = *value > ' ';
      break;
    }
  }
#endif

  return bFound;
}

/*
   @($) CGetCampoIni  FILES

   @(ID)
   Restituisce il nome del file che contiene il prefisso corrente.
   @(FD)

   @(ISV)
   s,s1 = stringhe di lavoro.

   Versione WIN32 e LINUX.
   @(FSV)
*/
const char* xvt_fsys_get_campo_ini()
{
  static char* prawin = NULL;
  if (prawin == NULL)
  {
    BOOLEAN bFound = FALSE;
    char exedir[_MAX_PATH], path[_MAX_PATH];
    // Nelle installazioni sfigate con programmi in rete cerca di stabilire il percorso locale di Campo.ini
    DIRECTORY dir;

    xvt_fsys_get_default_dir(&dir);
    xvt_fsys_convert_dir_to_str(&dir, exedir, sizeof(exedir));
#ifdef WIN32
    if (xvt_fsys_is_network_drive(exedir))
      bFound = xvt_fsys_get_campo_stp_value("CampoIni", path, sizeof(path));

    if (!bFound)
    {
      if (OsWin32_IsWindowsServer())
      {
        xvt_fsys_build_pathname(path, NULL, wxGetHomeDir(), "campo", "ini", NULL);
        bFound = xvt_fsys_file_exists(path);
        if (!bFound)
        {
				  char pathstd[_MAX_PATH];
   	  	  xvt_fsys_build_pathname(pathstd, NULL, exedir, "campo", "ini", NULL);
   	  	  if (xvt_fsys_file_exists(pathstd))
					  wxCopyFile(pathstd, path);
        }
      }
    }

		if (!bFound)
  	  xvt_fsys_build_pathname(path, NULL, exedir, "campo", "ini", NULL);
#else
		if (!bFound)
		{
			char username[32];
   		char ininame[_MAX_FNAME];

	    xvt_sys_get_user_name(username, sizeof(username));
			if (xvt_str_compare_ignoring_case(username, "root") == 0)
				*username = '\0';

	    sprintf(ininame, "campo%s", username);
  	  xvt_fsys_build_pathname(path, NULL, exedir, ininame, "ini", NULL);
   		if (!xvt_fsys_file_exists(path) && *username > ' ')
   		{
				char pathstd[_MAX_PATH];
   	  	xvt_fsys_build_pathname(pathstd, NULL, exedir, "campo", "ini", NULL);
   	  	if (xvt_fsys_file_exists(pathstd))
					wxCopyFile(pathstd, path);
			}
		}
#endif
 		if (!xvt_fsys_file_exists(path))
		{
   		char msg[256];
     	sprintf(msg, "Impossibile aprire '%s'", (const char *)path);
    	xvt_dm_post_fatal_exit(msg);
		}
    prawin = xvt_str_duplicate(path);
  }
  return prawin;
}

///////////////////////////////////////////////////////////
// Gestione MD5
///////////////////////////////////////////////////////////

#include "MD5Checksum.h"

BOOLEAN xvt_str_md5(const char* instr, char* outstr)
{
  wxASSERT(outstr != NULL);
  BOOLEAN ok = instr && *instr && outstr;
  if (ok)
  {
    wxStrcpy(outstr, wxMD5Checksum::GetMD5((unsigned char*)instr, strlen(instr)));
    ok = *outstr != '\0';
  }
  return ok;
}

BOOLEAN xvt_fsys_file_md5(const char* path, char* outstr)
{
  wxASSERT(outstr != NULL);
  BOOLEAN ok = path && *path && outstr;
  if (ok)
  {
    wxStrcpy(outstr, wxMD5Checksum::GetMD5(wxString(path)));
    ok = *outstr != '\0';
  }
  return ok;
}