Files
libreoffice/extensions/source/scanner/sane.cxx
Stephan Bergmann 314f15bff0 Extend loplugin:external to warn about enums
To mitigate the dangers of silently breaking ADL when moving enums into unnamed
namespaces (see the commit message of 206b5b2661
"New loplugin:external"), note all functions that are affected.  (The plan is to
extend loplugin:external further to also warn about classes and class templates,
and the code to identify affected functions already takes that into account, so
some parts of that code are not actually relevant for enums.)

But it appears that none of the functions that are actually affected by the
changes in this commit relied on being found through ADL, so no adaptions were
necessary for them.

(clang::DeclContext::collectAllContexts is non-const, which recursively means
that External's Visit... functions must take non-const Decl*.  Which required
compilerplugins/clang/sharedvisitor/analyzer.cxx to be generalized to support
such Visit... functions with non-const Decl* parameters.)

Change-Id: Ia215291402bf850d43defdab3cff4db5b270d1bd
Reviewed-on: https://gerrit.libreoffice.org/83001
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2019-11-17 00:28:17 +01:00

991 lines
34 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <cstdarg>
#include <type_traits>
#include <math.h>
#include <osl/file.h>
#include <sal/log.hxx>
#include <tools/stream.hxx>
#include <unotools/tempfile.hxx>
#include "sane.hxx"
#include <dlfcn.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sal/config.h>
#include <sal/macros.h>
#include <memory>
#if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
#include <stdarg.h>
#define dump_state( a, b, c, d ) fprintf( stderr, a, b, c, d );
#else
#define dump_state( a, b, c, d ) ;
#endif
static void dbg_msg( const char* pString, ... )
{
#if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
va_list ap;
va_start( ap, pString );
vfprintf( stderr, pString, ap );
va_end( ap );
#else
(void)pString;
#endif
}
#define FAIL_SHUTDOWN_STATE( x, y, z ) \
if( x != SANE_STATUS_GOOD ) \
{ \
dump_state( "%s returned error %d (%s)\n", \
y, x, p_strstatus( x ) ); \
DeInit(); \
return z; \
}
#define FAIL_STATE( x, y, z ) \
if( x != SANE_STATUS_GOOD ) \
{ \
dump_state( "%s returned error %d (%s)\n", \
y, x, p_strstatus( x ) ); \
return z; \
}
#define DUMP_STATE( x, y ) \
if( x != SANE_STATUS_GOOD ) \
{ \
dump_state( "%s returned error %d (%s)\n", \
y, x, p_strstatus( x ) ); \
}
int Sane::nRefCount = 0;
oslModule Sane::pSaneLib = nullptr;
SANE_Int Sane::nVersion = 0;
SANE_Device** Sane::ppDevices = nullptr;
int Sane::nDevices = 0;
SANE_Status (*Sane::p_init)( SANE_Int*,
SANE_Auth_Callback ) = nullptr;
void (*Sane::p_exit)() = nullptr;
SANE_Status (*Sane::p_get_devices)( const SANE_Device***,
SANE_Bool ) = nullptr;
SANE_Status (*Sane::p_open)( SANE_String_Const, SANE_Handle ) = nullptr;
void (*Sane::p_close)( SANE_Handle ) = nullptr;
const SANE_Option_Descriptor* (*Sane::p_get_option_descriptor)(
SANE_Handle, SANE_Int ) = nullptr;
SANE_Status (*Sane::p_control_option)( SANE_Handle, SANE_Int,
SANE_Action, void*,
SANE_Int* ) = nullptr;
SANE_Status (*Sane::p_get_parameters)( SANE_Handle,
SANE_Parameters* ) = nullptr;
SANE_Status (*Sane::p_start)( SANE_Handle ) = nullptr;
SANE_Status (*Sane::p_read)( SANE_Handle, SANE_Byte*, SANE_Int,
SANE_Int* ) = nullptr;
void (*Sane::p_cancel)( SANE_Handle ) = nullptr;
SANE_Status (*Sane::p_set_io_mode)( SANE_Handle, SANE_Bool ) = nullptr;
SANE_Status (*Sane::p_get_select_fd)( SANE_Handle, SANE_Int* ) = nullptr;
SANE_String_Const (*Sane::p_strstatus)( SANE_Status ) = nullptr;
static bool bSaneSymbolLoadFailed = false;
inline oslGenericFunction Sane::LoadSymbol( const char* pSymbolname )
{
oslGenericFunction pFunction = osl_getAsciiFunctionSymbol( pSaneLib, pSymbolname );
if( ! pFunction )
{
fprintf( stderr, "Could not load symbol %s\n",
pSymbolname );
bSaneSymbolLoadFailed = true;
}
return pFunction;
}
SANE_Status Sane::ControlOption( int nOption, SANE_Action nAction,
void* pData )
{
SANE_Int nInfo = 0;
SANE_Status nStatus = p_control_option( maHandle, static_cast<SANE_Int>(nOption),
nAction, pData, &nInfo );
DUMP_STATE( nStatus, "sane_control_option" );
#if OSL_DEBUG_LEVEL > 0
if( nStatus != SANE_STATUS_GOOD )
{
const char* pAction = "Unknown";
switch( nAction )
{
case SANE_ACTION_GET_VALUE:
pAction = "SANE_ACTION_GET_VALUE";break;
case SANE_ACTION_SET_VALUE:
pAction = "SANE_ACTION_SET_VALUE";break;
case SANE_ACTION_SET_AUTO:
pAction = "SANE_ACTION_SET_AUTO";break;
}
dbg_msg( "Option: \"%s\" action: %s\n",
OUStringToOString(GetOptionName(nOption), osl_getThreadTextEncoding()).getStr(),
pAction );
}
#endif
if( nInfo & SANE_INFO_RELOAD_OPTIONS )
ReloadOptions();
return nStatus;
}
Sane::Sane() :
mnOptions( 0 ),
mnDevice( -1 ),
maHandle( nullptr )
{
if( ! nRefCount || ! pSaneLib )
Init();
nRefCount++;
};
Sane::~Sane()
{
if( IsOpen() )
Close();
nRefCount--;
if( ! nRefCount && pSaneLib )
DeInit();
}
void Sane::Init()
{
OUString sSaneLibName( "libsane" SAL_DLLEXTENSION );
pSaneLib = osl_loadModule( sSaneLibName.pData, SAL_LOADMODULE_LAZY );
if( ! pSaneLib )
{
sSaneLibName = "libsane" SAL_DLLEXTENSION ".1";
pSaneLib = osl_loadModule( sSaneLibName.pData, SAL_LOADMODULE_LAZY );
}
// try reasonable places that might not be in the library search path
if( ! pSaneLib )
{
OUString sSaneLibSystemPath( "/usr/local/lib/libsane" SAL_DLLEXTENSION );
osl_getFileURLFromSystemPath( sSaneLibSystemPath.pData, &sSaneLibName.pData );
pSaneLib = osl_loadModule( sSaneLibName.pData, SAL_LOADMODULE_LAZY );
}
if( pSaneLib )
{
bSaneSymbolLoadFailed = false;
p_init = reinterpret_cast<SANE_Status(*)(SANE_Int*, SANE_Auth_Callback )>(
LoadSymbol( "sane_init" ));
p_exit = reinterpret_cast<void(*)()>(
LoadSymbol( "sane_exit" ));
p_get_devices = reinterpret_cast<SANE_Status(*)(const SANE_Device***,
SANE_Bool )>(
LoadSymbol( "sane_get_devices" ));
p_open = reinterpret_cast<SANE_Status(*)(SANE_String_Const, SANE_Handle )>(
LoadSymbol( "sane_open" ));
p_close = reinterpret_cast<void(*)(SANE_Handle)>(
LoadSymbol( "sane_close" ));
p_get_option_descriptor = reinterpret_cast<const SANE_Option_Descriptor*(*)(SANE_Handle,
SANE_Int)>(
LoadSymbol( "sane_get_option_descriptor" ));
p_control_option = reinterpret_cast<SANE_Status(*)(SANE_Handle, SANE_Int,
SANE_Action, void*, SANE_Int*)>(
LoadSymbol( "sane_control_option" ));
p_get_parameters = reinterpret_cast<SANE_Status(*)(SANE_Handle,SANE_Parameters*)>(
LoadSymbol( "sane_get_parameters" ));
p_start = reinterpret_cast<SANE_Status(*)(SANE_Handle)>(
LoadSymbol( "sane_start" ));
p_read = reinterpret_cast<SANE_Status(*)(SANE_Handle, SANE_Byte*,
SANE_Int, SANE_Int* )>(
LoadSymbol( "sane_read" ));
p_cancel = reinterpret_cast<void(*)(SANE_Handle)>(
LoadSymbol( "sane_cancel" ));
p_set_io_mode = reinterpret_cast<SANE_Status(*)(SANE_Handle, SANE_Bool)>(
LoadSymbol( "sane_set_io_mode" ));
p_get_select_fd = reinterpret_cast<SANE_Status(*)(SANE_Handle, SANE_Int*)>(
LoadSymbol( "sane_get_select_fd" ));
p_strstatus = reinterpret_cast<SANE_String_Const(*)(SANE_Status)>(
LoadSymbol( "sane_strstatus" ));
if( bSaneSymbolLoadFailed )
DeInit();
else
{
SANE_Status nStatus = p_init( &nVersion, nullptr );
FAIL_SHUTDOWN_STATE( nStatus, "sane_init", );
nStatus = p_get_devices( const_cast<const SANE_Device***>(&ppDevices),
SANE_FALSE );
FAIL_SHUTDOWN_STATE( nStatus, "sane_get_devices", );
for( nDevices = 0 ; ppDevices[ nDevices ]; nDevices++ ) ;
}
}
#if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
else
fprintf( stderr, "libsane%s could not be opened: %s\n", SAL_DLLEXTENSION,
dlerror() );
#endif
}
void Sane::DeInit()
{
if( pSaneLib )
{
p_exit();
osl_unloadModule( pSaneLib );
pSaneLib = nullptr;
}
}
void Sane::ReloadDevices()
{
if( IsOpen() )
Close();
DeInit();
Init();
}
void Sane::ReloadOptions()
{
if( ! IsOpen() )
return;
const SANE_Option_Descriptor* pZero = p_get_option_descriptor( maHandle, 0 );
SANE_Word pOptions[2];
SANE_Status nStatus = p_control_option( maHandle, 0, SANE_ACTION_GET_VALUE,
static_cast<void*>(pOptions), nullptr );
if( nStatus != SANE_STATUS_GOOD )
fprintf( stderr, "Error: sane driver returned %s while reading number of options !\n", p_strstatus( nStatus ) );
mnOptions = pOptions[ 0 ];
if( static_cast<size_t>(pZero->size) > sizeof( SANE_Word ) )
fprintf( stderr, "driver returned number of options with larger size than SANE_Word!!!\n" );
mppOptions.reset(new const SANE_Option_Descriptor*[ mnOptions ]);
mppOptions[ 0 ] = pZero;
for( int i = 1; i < mnOptions; i++ )
mppOptions[ i ] = p_get_option_descriptor( maHandle, i );
CheckConsistency( nullptr, true );
maReloadOptionsLink.Call( *this );
}
bool Sane::Open( const char* name )
{
SANE_Status nStatus = p_open( reinterpret_cast<SANE_String_Const>(name), &maHandle );
FAIL_STATE( nStatus, "sane_open", false );
ReloadOptions();
if( mnDevice == -1 )
{
OString aDevice( name );
for( int i = 0; i < nDevices; i++ )
{
if( aDevice == ppDevices[i]->name )
{
mnDevice = i;
break;
}
}
}
return true;
}
bool Sane::Open( int n )
{
if( n >= 0 && n < nDevices )
{
mnDevice = n;
return Open( ppDevices[n]->name );
}
return false;
}
void Sane::Close()
{
if( maHandle )
{
p_close( maHandle );
mppOptions.reset();
maHandle = nullptr;
mnDevice = -1;
}
}
int Sane::GetOptionByName( const char* rName )
{
int i;
OString aOption( rName );
for( i = 0; i < mnOptions; i++ )
{
if( mppOptions[i]->name && aOption == mppOptions[i]->name )
return i;
}
return -1;
}
bool Sane::GetOptionValue( int n, bool& rRet )
{
if( ! maHandle || mppOptions[n]->type != SANE_TYPE_BOOL )
return false;
SANE_Word nRet;
SANE_Status nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, &nRet );
if( nStatus != SANE_STATUS_GOOD )
return false;
rRet = nRet;
return true;
}
bool Sane::GetOptionValue( int n, OString& rRet )
{
bool bSuccess = false;
if( ! maHandle || mppOptions[n]->type != SANE_TYPE_STRING )
return false;
std::unique_ptr<char[]> pRet(new char[mppOptions[n]->size+1]);
SANE_Status nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, pRet.get() );
if( nStatus == SANE_STATUS_GOOD )
{
bSuccess = true;
rRet = pRet.get();
}
return bSuccess;
}
bool Sane::GetOptionValue( int n, double& rRet, int nElement )
{
bool bSuccess = false;
if( ! maHandle || ( mppOptions[n]->type != SANE_TYPE_INT &&
mppOptions[n]->type != SANE_TYPE_FIXED ) )
return false;
std::unique_ptr<SANE_Word[]> pRet(new SANE_Word[mppOptions[n]->size/sizeof(SANE_Word)]);
SANE_Status nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, pRet.get() );
if( nStatus == SANE_STATUS_GOOD )
{
bSuccess = true;
if( mppOptions[n]->type == SANE_TYPE_INT )
rRet = static_cast<double>(pRet[ nElement ]);
else
rRet = SANE_UNFIX( pRet[nElement] );
}
return bSuccess;
}
bool Sane::GetOptionValue( int n, double* pSet )
{
if( ! maHandle || ! ( mppOptions[n]->type == SANE_TYPE_FIXED ||
mppOptions[n]->type == SANE_TYPE_INT ) )
return false;
std::unique_ptr<SANE_Word[]> pFixedSet(new SANE_Word[mppOptions[n]->size/sizeof(SANE_Word)]);
SANE_Status nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, pFixedSet.get() );
if( nStatus != SANE_STATUS_GOOD )
return false;
for( size_t i = 0; i <mppOptions[n]->size/sizeof(SANE_Word); i++ )
{
if( mppOptions[n]->type == SANE_TYPE_FIXED )
pSet[i] = SANE_UNFIX( pFixedSet[i] );
else
pSet[i] = static_cast<double>(pFixedSet[i]);
}
return true;
}
void Sane::SetOptionValue( int n, bool bSet )
{
if( ! maHandle || mppOptions[n]->type != SANE_TYPE_BOOL )
return;
SANE_Word nRet = bSet ? SANE_TRUE : SANE_FALSE;
ControlOption( n, SANE_ACTION_SET_VALUE, &nRet );
}
void Sane::SetOptionValue( int n, const OUString& rSet )
{
if( ! maHandle || mppOptions[n]->type != SANE_TYPE_STRING )
return;
OString aSet(OUStringToOString(rSet, osl_getThreadTextEncoding()));
ControlOption( n, SANE_ACTION_SET_VALUE, const_cast<char *>(aSet.getStr()) );
}
void Sane::SetOptionValue( int n, double fSet, int nElement )
{
if( ! maHandle || ( mppOptions[n]->type != SANE_TYPE_INT &&
mppOptions[n]->type != SANE_TYPE_FIXED ) )
return;
if( mppOptions[n]->size/sizeof(SANE_Word) > 1 )
{
std::unique_ptr<SANE_Word[]> pSet(new SANE_Word[mppOptions[n]->size/sizeof(SANE_Word)]);
SANE_Status nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, pSet.get() );
if( nStatus == SANE_STATUS_GOOD )
{
pSet[nElement] = mppOptions[n]->type == SANE_TYPE_INT ?
static_cast<SANE_Word>(fSet) : SANE_FIX( fSet );
ControlOption( n, SANE_ACTION_SET_VALUE, pSet.get() );
}
}
else
{
SANE_Word nSetTo =
mppOptions[n]->type == SANE_TYPE_INT ?
static_cast<SANE_Word>(fSet) : SANE_FIX( fSet );
ControlOption( n, SANE_ACTION_SET_VALUE, &nSetTo );
}
}
void Sane::SetOptionValue( int n, double const * pSet )
{
if( ! maHandle || ( mppOptions[n]->type != SANE_TYPE_INT &&
mppOptions[n]->type != SANE_TYPE_FIXED ) )
return;
std::unique_ptr<SANE_Word[]> pFixedSet(new SANE_Word[mppOptions[n]->size/sizeof(SANE_Word)]);
for( size_t i = 0; i < mppOptions[n]->size/sizeof(SANE_Word); i++ )
{
if( mppOptions[n]->type == SANE_TYPE_FIXED )
pFixedSet[i] = SANE_FIX( pSet[i] );
else
pFixedSet[i] = static_cast<SANE_Word>(pSet[i]);
}
ControlOption( n, SANE_ACTION_SET_VALUE, pFixedSet.get() );
}
namespace {
enum FrameStyleType {
FrameStyle_BW, FrameStyle_Gray, FrameStyle_RGB, FrameStyle_Separated
};
}
#define BYTE_BUFFER_SIZE 32768
static sal_uInt8 ReadValue( FILE* fp, int depth )
{
if( depth == 16 )
{
sal_uInt16 nWord;
// data always come in native byte order !
// 16 bits is not really supported by backends as of now
// e.g. UMAX Astra 1200S delivers 16 bit but in BIGENDIAN
// against SANE documentation (xscanimage gets the same result
// as we do
size_t items_read = fread( &nWord, 1, 2, fp );
if (items_read != 2)
{
SAL_WARN( "extensions.scanner", "short read, abandoning" );
return 0;
}
return static_cast<sal_uInt8>( nWord / 256 );
}
sal_uInt8 nByte;
size_t items_read = fread( &nByte, 1, 1, fp );
if (items_read != 1)
{
SAL_WARN( "extensions.scanner", "short read, abandoning" );
return 0;
}
return nByte;
}
bool Sane::CheckConsistency( const char* pMes, bool bInit )
{
static const SANE_Option_Descriptor** pDescArray = nullptr;
static const SANE_Option_Descriptor* pZero = nullptr;
if( bInit )
{
pDescArray = mppOptions.get();
if( mppOptions )
pZero = mppOptions[0];
return true;
}
bool bConsistent = true;
if( pDescArray != mppOptions.get() )
bConsistent = false;
if( pZero != mppOptions[0] )
bConsistent = false;
if( ! bConsistent )
dbg_msg( "Sane is not consistent. (%s)\n", pMes );
return bConsistent;
}
bool Sane::Start( BitmapTransporter& rBitmap )
{
int nStream = 0, nLine = 0, i = 0;
SANE_Parameters aParams;
FrameStyleType eType = FrameStyle_Gray;
bool bSuccess = true;
bool bWidthSet = false;
if( ! maHandle )
return false;
int nWidthMM = 0;
int nHeightMM = 0;
double fTLx, fTLy, fResl = 0.0;
int nOption;
if( ( nOption = GetOptionByName( "tl-x" ) ) != -1 &&
GetOptionValue( nOption, fTLx ) &&
GetOptionUnit( nOption ) == SANE_UNIT_MM )
{
double fBRx;
if( ( nOption = GetOptionByName( "br-x" ) ) != -1 &&
GetOptionValue( nOption, fBRx ) &&
GetOptionUnit( nOption ) == SANE_UNIT_MM )
{
nWidthMM = static_cast<int>(fabs(fBRx - fTLx));
}
}
if( ( nOption = GetOptionByName( "tl-y" ) ) != -1 &&
GetOptionValue( nOption, fTLy ) &&
GetOptionUnit( nOption ) == SANE_UNIT_MM )
{
double fBRy;
if( ( nOption = GetOptionByName( "br-y" ) ) != -1 &&
GetOptionValue( nOption, fBRy ) &&
GetOptionUnit( nOption ) == SANE_UNIT_MM )
{
nHeightMM = static_cast<int>(fabs(fBRy - fTLy));
}
}
if( ( nOption = GetOptionByName( "resolution" ) ) != -1 )
(void)GetOptionValue( nOption, fResl );
std::unique_ptr<sal_uInt8[]> pBuffer;
SANE_Status nStatus = SANE_STATUS_GOOD;
rBitmap.lock();
SvMemoryStream& aConverter = rBitmap.getStream();
aConverter.Seek( 0 );
aConverter.SetEndian( SvStreamEndian::LITTLE );
// write bitmap stream header
aConverter.WriteChar( 'B' ).WriteChar( 'M' );
aConverter.WriteUInt32( 0 );
aConverter.WriteUInt32( 0 );
aConverter.WriteUInt32( 60 );
// write BITMAPINFOHEADER
aConverter.WriteUInt32( 40 );
aConverter.WriteUInt32( 0 ); // fill in width later
aConverter.WriteUInt32( 0 ); // fill in height later
aConverter.WriteUInt16( 1 );
// create header for 24 bits
// correct later if necessary
aConverter.WriteUInt16( 24 );
aConverter.WriteUInt32( 0 );
aConverter.WriteUInt32( 0 );
aConverter.WriteUInt32( 0 );
aConverter.WriteUInt32( 0 );
aConverter.WriteUInt32( 0 );
aConverter.WriteUInt32( 0 );
for( nStream=0; nStream < 3 && bSuccess ; nStream++ )
{
nStatus = p_start( maHandle );
DUMP_STATE( nStatus, "sane_start" );
CheckConsistency( "sane_start" );
if( nStatus == SANE_STATUS_GOOD )
{
nStatus = p_get_parameters( maHandle, &aParams );
DUMP_STATE( nStatus, "sane_get_parameters" );
CheckConsistency( "sane_get_parameters" );
if (nStatus != SANE_STATUS_GOOD || aParams.bytes_per_line == 0)
{
bSuccess = false;
break;
}
#if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
const char* const ppFormats[] = { "SANE_FRAME_GRAY", "SANE_FRAME_RGB",
"SANE_FRAME_RED", "SANE_FRAME_GREEN",
"SANE_FRAME_BLUE", "Unknown !!!" };
fprintf( stderr, "Parameters for frame %d:\n", nStream );
if( static_cast<
typename std::make_unsigned<
typename std::underlying_type<SANE_Frame>::type>::type>(
aParams.format)
> 4 )
{
aParams.format = SANE_Frame(5);
}
fprintf( stderr, "format: %s\n", ppFormats[ static_cast<int>(aParams.format) ] );
fprintf( stderr, "last_frame: %s\n", aParams.last_frame ? "TRUE" : "FALSE" );
fprintf( stderr, "depth: %d\n", static_cast<int>(aParams.depth) );
fprintf( stderr, "pixels_per_line: %d\n", static_cast<int>(aParams.pixels_per_line) );
fprintf( stderr, "bytes_per_line: %d\n", static_cast<int>(aParams.bytes_per_line) );
#endif
if( ! pBuffer )
{
pBuffer.reset(new sal_uInt8[ BYTE_BUFFER_SIZE < 4*aParams.bytes_per_line ? 4*aParams.bytes_per_line : BYTE_BUFFER_SIZE ]);
}
if( aParams.last_frame )
nStream=3;
switch( aParams.format )
{
case SANE_FRAME_GRAY:
eType = FrameStyle_Gray;
if( aParams.depth == 1 )
eType = FrameStyle_BW;
break;
case SANE_FRAME_RGB:
eType = FrameStyle_RGB;
break;
case SANE_FRAME_RED:
case SANE_FRAME_GREEN:
case SANE_FRAME_BLUE:
eType = FrameStyle_Separated;
break;
default:
fprintf( stderr, "Warning: unknown frame style !!!\n" );
}
bool bSynchronousRead = true;
// should be fail safe, but ... ??
nStatus = p_set_io_mode( maHandle, SANE_FALSE );
CheckConsistency( "sane_set_io_mode" );
if( nStatus != SANE_STATUS_GOOD )
{
bSynchronousRead = false;
nStatus = p_set_io_mode(maHandle, SANE_TRUE);
CheckConsistency( "sane_set_io_mode" );
if (nStatus != SANE_STATUS_GOOD)
{
SAL_WARN("extensions.scanner", "SANE driver status is: " << nStatus);
}
}
SANE_Int nLen=0;
SANE_Int fd = 0;
if( ! bSynchronousRead )
{
nStatus = p_get_select_fd( maHandle, &fd );
DUMP_STATE( nStatus, "sane_get_select_fd" );
CheckConsistency( "sane_get_select_fd" );
if( nStatus != SANE_STATUS_GOOD )
bSynchronousRead = true;
}
utl::TempFile aFrame;
aFrame.EnableKillingFile();
FILE* pFrame = fopen(OUStringToOString(aFrame.GetFileName(), osl_getThreadTextEncoding()).getStr(), "w+b");
if( ! pFrame )
{
bSuccess = false;
break;
}
do {
if( ! bSynchronousRead )
{
fd_set fdset;
struct timeval tv;
FD_ZERO( &fdset );
FD_SET( static_cast<int>(fd), &fdset );
tv.tv_sec = 5;
tv.tv_usec = 0;
if( select( fd+1, &fdset, nullptr, nullptr, &tv ) == 0 )
fprintf( stderr, "Timeout on sane_read descriptor\n" );
}
nLen = 0;
nStatus = p_read( maHandle, pBuffer.get(), BYTE_BUFFER_SIZE, &nLen );
CheckConsistency( "sane_read" );
if( nLen && ( nStatus == SANE_STATUS_GOOD ||
nStatus == SANE_STATUS_EOF ) )
{
bSuccess = (static_cast<size_t>(nLen) == fwrite( pBuffer.get(), 1, nLen, pFrame ));
if (!bSuccess)
break;
}
else
DUMP_STATE( nStatus, "sane_read" );
} while( nStatus == SANE_STATUS_GOOD );
if (nStatus != SANE_STATUS_EOF || !bSuccess)
{
fclose( pFrame );
bSuccess = false;
break;
}
int nFrameLength = ftell( pFrame );
fseek( pFrame, 0, SEEK_SET );
sal_uInt32 nWidth = static_cast<sal_uInt32>(aParams.pixels_per_line);
sal_uInt32 nHeight = static_cast<sal_uInt32>(nFrameLength / aParams.bytes_per_line);
if( ! bWidthSet )
{
if( ! fResl )
fResl = 300; // if all else fails that's a good guess
if( ! nWidthMM )
nWidthMM = static_cast<int>((static_cast<double>(nWidth) / fResl) * 25.4);
if( ! nHeightMM )
nHeightMM = static_cast<int>((static_cast<double>(nHeight) / fResl) * 25.4);
SAL_INFO("extensions.scanner", "set dimensions to(" << nWidth << ", " << nHeight << ") Pixel, (" << nWidthMM << ", " << nHeightMM <<
") mm, resolution is " << fResl);
aConverter.Seek( 18 );
aConverter.WriteUInt32( nWidth );
aConverter.WriteUInt32( nHeight );
aConverter.Seek( 38 );
aConverter.WriteUInt32( 1000*nWidth/nWidthMM );
aConverter.WriteUInt32( 1000*nHeight/nHeightMM );
bWidthSet = true;
}
aConverter.Seek(60);
if( eType == FrameStyle_BW )
{
aConverter.Seek( 10 );
aConverter.WriteUInt32( 64 );
aConverter.Seek( 28 );
aConverter.WriteUInt16( 1 );
aConverter.Seek( 54 );
// write color table
aConverter.WriteUInt16( 0xffff );
aConverter.WriteUChar( 0xff );
aConverter.WriteUChar( 0 );
aConverter.WriteUInt32( 0 );
aConverter.Seek( 64 );
}
else if( eType == FrameStyle_Gray )
{
aConverter.Seek( 10 );
aConverter.WriteUInt32( 1084 );
aConverter.Seek( 28 );
aConverter.WriteUInt16( 8 );
aConverter.Seek( 54 );
// write color table
for( nLine = 0; nLine < 256; nLine++ )
{
aConverter.WriteUChar( nLine );
aConverter.WriteUChar( nLine );
aConverter.WriteUChar( nLine );
aConverter.WriteUChar( 0 );
}
aConverter.Seek( 1084 );
}
for (nLine = nHeight-1; nLine >= 0; --nLine)
{
if (fseek(pFrame, nLine * aParams.bytes_per_line, SEEK_SET) == -1)
{
bSuccess = false;
break;
}
if( eType == FrameStyle_BW ||
( eType == FrameStyle_Gray && aParams.depth == 8 )
)
{
SANE_Int items_read = fread( pBuffer.get(), 1, aParams.bytes_per_line, pFrame );
if (items_read != aParams.bytes_per_line)
{
SAL_WARN( "extensions.scanner", "short read, padding with zeros" );
memset(pBuffer.get() + items_read, 0, aParams.bytes_per_line - items_read);
}
aConverter.WriteBytes(pBuffer.get(), aParams.bytes_per_line);
}
else if( eType == FrameStyle_Gray )
{
for( i = 0; i < (aParams.pixels_per_line); i++ )
{
sal_uInt8 nGray = ReadValue( pFrame, aParams.depth );
aConverter.WriteUChar( nGray );
}
}
else if( eType == FrameStyle_RGB )
{
for( i = 0; i < (aParams.pixels_per_line); i++ )
{
sal_uInt8 nRed, nGreen, nBlue;
nRed = ReadValue( pFrame, aParams.depth );
nGreen = ReadValue( pFrame, aParams.depth );
nBlue = ReadValue( pFrame, aParams.depth );
aConverter.WriteUChar( nBlue );
aConverter.WriteUChar( nGreen );
aConverter.WriteUChar( nRed );
}
}
else if( eType == FrameStyle_Separated )
{
for( i = 0; i < (aParams.pixels_per_line); i++ )
{
sal_uInt8 nValue = ReadValue( pFrame, aParams.depth );
switch( aParams.format )
{
case SANE_FRAME_RED:
aConverter.SeekRel( 2 );
aConverter.WriteUChar( nValue );
break;
case SANE_FRAME_GREEN:
aConverter.SeekRel( 1 );
aConverter.WriteUChar( nValue );
aConverter.SeekRel( 1 );
break;
case SANE_FRAME_BLUE:
aConverter.WriteUChar( nValue );
aConverter.SeekRel( 2 );
break;
case SANE_FRAME_GRAY:
case SANE_FRAME_RGB:
break;
}
}
}
int nGap = aConverter.Tell() & 3;
if (nGap)
aConverter.SeekRel( 4-nGap );
}
fclose( pFrame ); // deletes tmpfile
if( eType != FrameStyle_Separated )
break;
}
else
bSuccess = false;
}
// get stream length
int nPos = aConverter.TellEnd();
aConverter.Seek( 2 );
aConverter.WriteUInt32( nPos+1 );
aConverter.Seek( 0 );
rBitmap.unlock();
if( bSuccess )
{
// only cancel a successful operation
// sane disrupts memory else
p_cancel( maHandle );
CheckConsistency( "sane_cancel" );
}
pBuffer.reset();
ReloadOptions();
dbg_msg( "Sane::Start returns with %s\n", bSuccess ? "TRUE" : "FALSE" );
return bSuccess;
}
int Sane::GetRange( int n, std::unique_ptr<double[]>& rpDouble )
{
if( mppOptions[n]->constraint_type != SANE_CONSTRAINT_RANGE &&
mppOptions[n]->constraint_type != SANE_CONSTRAINT_WORD_LIST )
{
return -1;
}
rpDouble = nullptr;
int nItems, i;
bool bIsFixed = mppOptions[n]->type == SANE_TYPE_FIXED;
dbg_msg( "Sane::GetRange of option %s ", mppOptions[n]->name );
if(mppOptions[n]->constraint_type == SANE_CONSTRAINT_RANGE )
{
double fMin, fMax, fQuant;
if( bIsFixed )
{
fMin = SANE_UNFIX( mppOptions[n]->constraint.range->min );
fMax = SANE_UNFIX( mppOptions[n]->constraint.range->max );
fQuant = SANE_UNFIX( mppOptions[n]->constraint.range->quant );
}
else
{
fMin = static_cast<double>(mppOptions[n]->constraint.range->min);
fMax = static_cast<double>(mppOptions[n]->constraint.range->max);
fQuant = static_cast<double>(mppOptions[n]->constraint.range->quant);
}
if( fQuant != 0.0 )
{
dbg_msg( "quantum range [ %lg ; %lg ; %lg ]\n",
fMin, fQuant, fMax );
nItems = static_cast<int>((fMax - fMin)/fQuant)+1;
rpDouble.reset(new double[ nItems ]);
double fValue = fMin;
for( i = 0; i < nItems; i++, fValue += fQuant )
rpDouble[i] = fValue;
rpDouble[ nItems-1 ] = fMax;
return nItems;
}
else
{
dbg_msg( "normal range [ %lg %lg ]\n",
fMin, fMax );
rpDouble.reset(new double[2]);
rpDouble[0] = fMin;
rpDouble[1] = fMax;
return 0;
}
}
else
{
nItems = mppOptions[n]->constraint.word_list[0];
rpDouble.reset(new double[nItems]);
for( i=0; i<nItems; i++ )
{
rpDouble[i] = bIsFixed ?
SANE_UNFIX( mppOptions[n]->constraint.word_list[i+1] ) :
static_cast<double>(mppOptions[n]->constraint.word_list[i+1]);
}
dbg_msg( "wordlist [ %lg ... %lg ]\n",
rpDouble[ 0 ], rpDouble[ nItems-1 ] );
return nItems;
}
}
static const char *ppUnits[] = {
"",
"[Pixel]",
"[Bit]",
"[mm]",
"[DPI]",
"[%]",
"[usec]"
};
OUString Sane::GetOptionUnitName( int n )
{
OUString aText;
SANE_Unit nUnit = mppOptions[n]->unit;
size_t nUnitAsSize = static_cast<size_t>(nUnit);
if (nUnitAsSize >= SAL_N_ELEMENTS( ppUnits ))
aText = "[unknown units]";
else
aText = OUString( ppUnits[ nUnit ], strlen(ppUnits[ nUnit ]), osl_getThreadTextEncoding() );
return aText;
}
bool Sane::ActivateButtonOption( int n )
{
SANE_Status nStatus = ControlOption( n, SANE_ACTION_SET_VALUE, nullptr );
return nStatus == SANE_STATUS_GOOD;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */