text:
use super::errors::try_error;
use crate::errors::invalid_data_error;
use crate::{util, Message, Messages};
use crate::{vchan, Payload};
use bitflags::bitflags;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use num_traits::FromPrimitive;
use rdp::core::{mcs, tpkt};
use rdp::model::error::*;
use std::collections::{HashMap, VecDeque};
use std::io::{Read, Write};

pub const CHANNEL_NAME: &str = "cliprdr";

/// Client implements a client for the clipboard virtual channel
/// (CLIPRDR) extension, as defined in:
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeclip/fb9b7e0b-6db4-41c2-b83c-f889c1ee7688
pub struct Client {
    clipboard: HashMap<u32, Vec<u8>>,
    on_remote_copy: Box<dyn Fn(Vec<u8>) -> RdpResult<()>>,
    vchan: vchan::Client,
    incoming_paste_formats: VecDeque<ClipboardFormat>,
}

impl Default for Client {
    fn default() -> Self {
        Self::new(Box::new(|_| Ok(())))
    }
}

impl Client {
    pub fn new(on_remote_copy: Box<dyn Fn(Vec<u8>) -> RdpResult<()>>) -> Self {
        Client {
            clipboard: HashMap::new(),
            on_remote_copy,
            vchan: vchan::Client::new(),
            incoming_paste_formats: VecDeque::new(),
        }
    }
    /// Reads raw RDP messages sent on the cliprdr virtual channel and replies as necessary.
    pub fn read_and_reply<S: Read + Write>(
        &mut self,
        payload: tpkt::Payload,
        mcs: &mut mcs::Client<S>,
    ) -> RdpResult<()> {
        if let Some(mut payload) = self.vchan.read(payload)? {
            let header = ClipboardPDUHeader::decode(&mut payload)?;

            debug!("received {:?}", header.msg_type);

            let responses = match header.msg_type {
                ClipboardPDUType::CB_CLIP_CAPS => self.handle_server_caps(&mut payload)?,
                ClipboardPDUType::CB_MONITOR_READY => self.handle_monitor_ready(&mut payload)?,
                ClipboardPDUType::CB_FORMAT_LIST => {
                    self.handle_format_list(&mut payload, header.data_len)?
                }
                ClipboardPDUType::CB_FORMAT_LIST_RESPONSE => {
                    self.handle_format_list_response(header.msg_flags)?
                }
                ClipboardPDUType::CB_FORMAT_DATA_REQUEST => {
                    self.handle_format_data_request(&mut payload)?
                }
                ClipboardPDUType::CB_FORMAT_DATA_RESPONSE => {
                    if header
                        .msg_flags
                        .contains(ClipboardHeaderFlags::CB_RESPONSE_OK)
                    {
                        self.handle_format_data_response(&mut payload, header.data_len)?
                    } else {
                        warn!("RDP server failed to process format data request");
                        vec![]
                    }
                }
                _ => {
                    warn!(
                        "CLIPRDR message {:?} not implemented, ignoring",
                        header.msg_type
                    );
                    vec![]
                }
            };

            let chan = &CHANNEL_NAME.to_string();
            for resp in responses {
                mcs.write(chan, resp)?;
            }
        }

        Ok(())
    }

    /// update_clipboard is invoked from Go.
    /// It updates the local clipboard cache and returns the encoded message
    /// that should be sent to the RDP server.
    pub fn update_clipboard(&mut self, data: String) -> RdpResult<Messages> {
        // convert LF to CRLF, as required by CF_TEXT and CF_UNICODETEXT
        let mut converted = String::with_capacity(data.len());
        let mut prev_was_cr = false;
        for c in data.chars() {
            match c {
                '\n' if !prev_was_cr => {
                    // convert LF to CRLF, so long as the previous character
                    // wasn't CR (in which case there's no conversion necessary)
                    converted.push('\r');
                    converted.push('\n');
                }
                c => {
                    converted.push(c);
                    if c == '\r' {
                        prev_was_cr = true;
                        continue;
                    }
                }
            }

            prev_was_cr = false;
        }

        let (data, format) = encode_clipboard(converted);
        self.clipboard.insert(format as u32, data);
        self.add_headers_and_chunkify(
            ClipboardPDUType::CB_FORMAT_LIST,
            FormatListPDU {
                format_names: vec![LongFormatName::id(format as u32)],
            }
            .encode()?,
        )
    }

    /// Handles the server capabilities message, which is the first message sent from the server
    /// to the client during the initialization sequence. Described in section 1.3.2.1.
    fn handle_server_caps(&self, payload: &mut Payload) -> RdpResult<Messages> {
        let caps = ClipboardCapabilitiesPDU::decode(payload)?;
        if let Some(general) = caps.general {
            // our capabilities are minimal, so we log the server
            // capabilities for debug purposes, but don't otherwise care
            // (the server will be forced into working with us)
            info!("RDP server clipboard capabilities: {:?}", general);
        }

        // we don't send our capabilities here, they get sent as a response
        // to the monitor ready PDU below
        Ok(vec![])
    }

    /// Handles the monitor ready PDU, which is sent from the server to the client during
    /// the initialization phase. Upon receiving this message, the client should respond
    /// with its capabilities, an optional temporary directory PDU, and a format list PDU.
    fn handle_monitor_ready(&self, _payload: &mut Payload) -> RdpResult<Messages> {
        // There's nothing additional to decode here, the monitor ready PDU is just a header.
        // In response, we need to:
        // 1. Send our clipboard capabilities
        // 2. Mimic a "copy" operation by sending a format list PDU
        // This completes the initialization process.
        let mut result = self.add_headers_and_chunkify(
            ClipboardPDUType::CB_CLIP_CAPS,
            ClipboardCapabilitiesPDU {
                general: Some(GeneralClipboardCapabilitySet {
                    version: CB_CAPS_VERSION_2,
                    flags: ClipboardGeneralCapabilityFlags::CB_USE_LONG_FORMAT_NAMES,
                }),
            }
            .encode()?,
        )?;
        result.extend(
            self.add_headers_and_chunkify(
                ClipboardPDUType::CB_FORMAT_LIST,
                FormatListPDU::<LongFormatName> {
                    format_names: vec![LongFormatName::id(0)],
                }
                .encode()?,
            )?,
        );

        Ok(result)
    }

    /// Handles the format list PDU, which is a notification from the server
    /// that some data was copied and can be requested at a later date.
    fn handle_format_list(&mut self, payload: &mut Payload, length: u32) -> RdpResult<Messages> {
        let list = FormatListPDU::<LongFormatName>::decode(payload, length)?;
        let formats = list
            .format_names
            .iter()
            .map(|n| n.format_id)
            .collect::<Vec<u32>>();

        debug!("{:?} data was copied on the RDP server", formats);
        let mut result =
            self.add_headers_and_chunkify(ClipboardPDUType::CB_FORMAT_LIST_RESPONSE, vec![])?;

        let request_format;
        if formats.contains(&(ClipboardFormat::CF_UNICODETEXT as u32)) {
            request_format = ClipboardFormat::CF_UNICODETEXT;
        } else if formats.contains(&(ClipboardFormat::CF_TEXT as u32)) {
            request_format = ClipboardFormat::CF_TEXT;
        } else if formats.contains(&(ClipboardFormat::CF_OEMTEXT as u32)) {
            request_format = ClipboardFormat::CF_OEMTEXT;
        } else {
            info!(
                "{:?} data was copied on the remote desktop, but no supported formats were found",
                formats
            );

            return Ok(result);
        }

        // Record the format of the data we're requesting so we can correcly decode the response later.
        // Response events are globally ordered so we use a FIFO queue for format tracking.
        self.incoming_paste_formats.push_back(request_format);

        // request the data by imitating a paste event.
        result.extend(self.add_headers_and_chunkify(
            ClipboardPDUType::CB_FORMAT_DATA_REQUEST,
            FormatDataRequestPDU::for_id(request_format as u32).encode()?,
        )?);

        Ok(result)
    }

    /// Handle the format list response, which is the server acknowledging that
    /// it recieved a notification that the client has updated clipboard data
    /// that may be requested in the future.
    fn handle_format_list_response(&self, flags: ClipboardHeaderFlags) -> RdpResult<Messages> {
        if !flags.contains(ClipboardHeaderFlags::CB_RESPONSE_OK) {
            warn!("RDP server did not process our copy operation");
        }
        Ok(vec![])
    }

    /// Handles a request from the RDP server for clipboard data.
    /// This message is received when a user executes a paste in the remote desktop.
    fn handle_format_data_request(&self, payload: &mut Payload) -> RdpResult<Messages> {
        let req = FormatDataRequestPDU::decode(payload)?;
        let data = match self.clipboard.get(&req.format_id) {
            Some(d) => d.clone(),
            // TODO(zmb3): send empty FORMAT_DATA_RESPONSE with RESPONSE_FAIL flag set in header
            None => {
                return Err(invalid_data_error(
                    format!(
                        "clipboard does not contain data for format {}",
                        req.format_id
                    )
                    .as_str(),
                ))
            }
        };

        self.add_headers_and_chunkify(
            ClipboardPDUType::CB_FORMAT_DATA_RESPONSE,
            FormatDataResponsePDU { data }.encode()?,
        )
    }

    /// Receives clipboard data from the remote desktop. This is the server responding
    /// to our format data request.
    fn handle_format_data_response(
        &mut self,
        payload: &mut Payload,
        length: u32,
    ) -> RdpResult<Messages> {
        let resp = FormatDataResponsePDU::decode(payload, length)?;
        let data_len = resp.data.len();
        let format = self.incoming_paste_formats.pop_front().ok_or_else(|| {
            try_error("no expected format found, possibly received too many format data responses")
        })?;

        debug!(
            "received {} bytes of copied data from Windows Desktop with format {:?}",
            data_len, format,
        );

        let decoded = decode_clipboard(resp.data, format)?;
        (self.on_remote_copy)(decoded)?;
        Ok(vec![])
    }

    /// add_headers_and_chunkify takes an encoded PDU ready to be sent over a virtual channel (payload),
    /// adds on the Clipboard PDU Header based the passed msg_type, adds the appropriate (virtual) Channel PDU Header,
    /// and splits the entire payload into chunks if the payload exceeds the maximum size.
    fn add_headers_and_chunkify(
        &self,
        msg_type: ClipboardPDUType,
        payload: Message,
    ) -> RdpResult<Messages> {
        let msg_flags = match msg_type {
            // the spec requires 0 for these messages
            ClipboardPDUType::CB_CLIP_CAPS => ClipboardHeaderFlags::from_bits_truncate(0),
            ClipboardPDUType::CB_TEMP_DIRECTORY => ClipboardHeaderFlags::from_bits_truncate(0),
            ClipboardPDUType::CB_LOCK_CLIPDATA => ClipboardHeaderFlags::from_bits_truncate(0),
            ClipboardPDUType::CB_UNLOCK_CLIPDATA => ClipboardHeaderFlags::from_bits_truncate(0),
            ClipboardPDUType::CB_FORMAT_DATA_REQUEST => ClipboardHeaderFlags::from_bits_truncate(0),

            // assume success for now
            ClipboardPDUType::CB_FORMAT_DATA_RESPONSE => ClipboardHeaderFlags::CB_RESPONSE_OK,
            ClipboardPDUType::CB_FORMAT_LIST_RESPONSE => ClipboardHeaderFlags::CB_RESPONSE_OK,

            // we don't advertise support for file transfers, so the server should never send this,
            // but if it does, ensure the response indicates a failure
            ClipboardPDUType::CB_FILECONTENTS_RESPONSE => ClipboardHeaderFlags::CB_RESPONSE_FAIL,

            _ => ClipboardHeaderFlags::from_bits_truncate(0),
        };

        let channel_flags = match msg_type {
            ClipboardPDUType::CB_FORMAT_LIST
            | ClipboardPDUType::CB_CLIP_CAPS
            | ClipboardPDUType::CB_FORMAT_DATA_REQUEST
            | ClipboardPDUType::CB_FORMAT_DATA_RESPONSE => {
                Some(vchan::ChannelPDUFlags::CHANNEL_FLAG_SHOW_PROTOCOL)
            }
            _ => None,
        };

        let mut inner =
            ClipboardPDUHeader::new(msg_type, msg_flags, payload.len() as u32).encode()?;
        inner.extend(payload);

        self.vchan.add_header_and_chunkify(channel_flags, inner)
    }
}

bitflags! {
    struct ClipboardHeaderFlags: u16 {
        /// Indicates that the assocated request was processed successfully.
        const CB_RESPONSE_OK = 0x0001;

        /// Indicates that the associated request was not procesed successfully.
        const CB_RESPONSE_FAIL = 0x0002;

        /// Used by the short format name variant to indicate that the format
        /// names are in ASCII 8.
        const CB_ASCII_NAMES = 0x0004;
    }
}

/// This header (CLIPRDR_HEADER) is present in all clipboard PDUs.
#[derive(Debug, PartialEq, Eq)]
struct ClipboardPDUHeader {
    /// Specifies the type of clipboard PDU that follows the dataLen field.
    msg_type: ClipboardPDUType,
    msg_flags: ClipboardHeaderFlags,
    /// Specifies the size, in bytes, of the data which follows this header.
    data_len: u32,
}

impl ClipboardPDUHeader {
    fn new(msg_type: ClipboardPDUType, msg_flags: ClipboardHeaderFlags, data_len: u32) -> Self {
        ClipboardPDUHeader {
            msg_type,
            msg_flags,
            data_len,
        }
    }

    fn encode(&self) -> RdpResult<Message> {
        let mut w = vec![];
        w.write_u16::<LittleEndian>(self.msg_type as u16)?;
        w.write_u16::<LittleEndian>(self.msg_flags.bits())?;
        w.write_u32::<LittleEndian>(self.data_len)?;
        Ok(w)
    }

    fn decode(payload: &mut Payload) -> RdpResult<Self> {
        let typ = payload.read_u16::<LittleEndian>()?;
        Ok(Self {
            msg_type: ClipboardPDUType::from_u16(typ)
                .ok_or_else(|| invalid_data_error(&format!("invalid message type {:#04x}", typ)))?,
            msg_flags: ClipboardHeaderFlags::from_bits(payload.read_u16::<LittleEndian>()?)
                .ok_or_else(|| invalid_data_error("invalid flags in clipboard header"))?,
            data_len: payload.read_u32::<LittleEndian>()?,
        })
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)]
#[allow(non_camel_case_types)]
enum ClipboardPDUType {
    CB_MONITOR_READY = 0x0001,
    CB_FORMAT_LIST = 0x0002,
    CB_FORMAT_LIST_RESPONSE = 0x0003,
    CB_FORMAT_DATA_REQUEST = 0x0004,
    CB_FORMAT_DATA_RESPONSE = 0x0005,
    CB_TEMP_DIRECTORY = 0x0006,
    CB_CLIP_CAPS = 0x0007,
    CB_FILECONTENTS_REQUEST = 0x0008,
    CB_FILECONTENTS_RESPONSE = 0x0009,
    CB_LOCK_CLIPDATA = 0x000A,
    CB_UNLOCK_CLIPDATA = 0x000B,
}

/// An optional PDU (CLIPRDR_CAPS) used to exchange capability information.
/// If this PDU is not sent, it is assumed that the endpoint which did not
/// send capabilities is using the default values for each field.
#[derive(Debug)]
struct ClipboardCapabilitiesPDU {
    // The protocol is written in such a way that there can be
    // a variable number of capability sets in this PDU. However,
    // the spec only defines one type of capability set (general),
    // so we'll just use an Option.
    general: Option<GeneralClipboardCapabilitySet>,
}

const CB_CAPS_VERSION_2: u32 = 0x0002;

impl ClipboardCapabilitiesPDU {
    fn encode(&self) -> RdpResult<Message> {
        let mut w = vec![];
        // there's either 0 or 1 capability sets included here
        w.write_u16::<LittleEndian>(self.general.is_some() as u16)?;
        w.write_u16::<LittleEndian>(0)?; // pad

        if let Some(set) = &self.general {
            w.write_u16::<LittleEndian>(ClipboardCapabilitySetType::General as u16)?;
            w.write_u16::<LittleEndian>(12)?; // length
            w.write_u32::<LittleEndian>(CB_CAPS_VERSION_2)?;
            w.write_u32::<LittleEndian>(set.flags.bits)?;
        }

        Ok(w)
    }

    fn decode(payload: &mut Payload) -> RdpResult<Self> {
        let count = payload.read_u16::<LittleEndian>()?;
        payload.read_u16::<LittleEndian>()?; // pad

        match count {
            0 => Ok(Self { general: None }),
            1 => Ok(Self {
                general: Some(GeneralClipboardCapabilitySet::decode(payload)?),
            }),
            _ => Err(invalid_data_error("expected 0 or 1 capabilities")),
        }
    }
}

impl GeneralClipboardCapabilitySet {
    fn decode(payload: &mut Payload) -> RdpResult<Self> {
        let set_type = payload.read_u16::<LittleEndian>()?;
        if set_type != ClipboardCapabilitySetType::General as u16 {
            return Err(invalid_data_error(&format!(
                "expected general capability set (1), got {}",
                set_type
            )));
        }

        let length = payload.read_u16::<LittleEndian>()?;
        if length != 12u16 {
            return Err(invalid_data_error(
                "expected 12 bytes for the general capability set",
            ));
        }

        Ok(Self {
            version: payload.read_u32::<LittleEndian>()?,
            flags: ClipboardGeneralCapabilityFlags::from_bits(payload.read_u32::<LittleEndian>()?)
                .ok_or_else(|| invalid_data_error("invalid flags in general capability set"))?,
        })
    }
}

enum ClipboardCapabilitySetType {
    General = 0x0001,
}

/// The general capability set (CLIPRDR_GENERAL_CAPABILITY) is used to
/// advertise general clipboard settings.
#[derive(Debug)]
#[allow(dead_code)]
struct GeneralClipboardCapabilitySet {
    /// Specifies the RDP Clipboard Virtual Extension version number.
    /// Used for informational purposes only, and MUST NOT be used to
    /// make protocol capability decisions.
    version: u32,
    flags: ClipboardGeneralCapabilityFlags,
}

bitflags! {
    struct ClipboardGeneralCapabilityFlags: u32 {
        /// Indicates that long format names will be used in the format list PDU.
        /// If this flag is not set, then the short format names MUST be used.
        const CB_USE_LONG_FORMAT_NAMES = 0x0002;

        /// File copy and paste using stream-based operations are supported.
        const CB_STREAM_FILECLIP_ENABLED = 0x0004;

        /// Indicates that any description of files to copy and paste MUST NOT
        /// include the source path of the files.
        const CB_FILECLIP_NO_FILE_PATHS = 0x0008;

        /// Indicates that locking and unlocking of file stream data
        /// on the clipboard is supported.
        const CB_CAN_LOCK_CLIPDATA = 0x0010;

        /// Indicates support for transferring files greater than 4GB.
        const CB_HUGE_FILE_SUPPORT_ENABLED = 0x0020;
    }
}

/// The format list PDU is sent by either the client or server when
/// its local system clipboard is updated with new clipboard data.
///
/// It contains 0 or more format names, which are either all short
/// format or all long format depending on the server/client capabilities.
#[derive(Debug)]
struct FormatListPDU<T: FormatName> {
    format_names: Vec<T>,
}

trait FormatName: Sized {
    fn encode(&self) -> RdpResult<Message>;
    fn decode(payload: &mut Payload) -> RdpResult<Self>;
}

impl<T: FormatName> FormatListPDU<T> {
    fn encode(&self) -> RdpResult<Message> {
        let mut w = Vec::new();
        for name in &self.format_names {
            w.extend(name.encode()?);
        }

        Ok(w)
    }

    fn decode(payload: &mut Payload, length: u32) -> RdpResult<Self> {
        let mut format_names: Vec<T> = Vec::new();

        let startpos = payload.position();
        while payload.position() - startpos < length as u64 {
            format_names.push(T::decode(payload)?);
        }

        Ok(Self { format_names })
    }
}

// encode_clipboard encodes data suitably for clipboard storage.
// This means determining the appropriate format and making sure the data has a nul terminator.
//
// This data must be valid UTF-8.
fn encode_clipboard(mut data: String) -> (Vec<u8>, ClipboardFormat) {
    if data.is_ascii() {
        if matches!(data.chars().last(), Some(x) if x != '\0') {
            data.push('\0');
        }

        (data.into_bytes(), ClipboardFormat::CF_TEXT)
    } else {
        let encoded = util::to_unicode(&data, true);
        (encoded, ClipboardFormat::CF_UNICODETEXT)
    }
}

// decode_clipboard decodes data from a given clipboard format into UTF-8.
fn decode_clipboard(mut data: Vec<u8>, format: ClipboardFormat) -> RdpResult<Vec<u8>> {
    match format {
        ClipboardFormat::CF_TEXT | ClipboardFormat::CF_OEMTEXT => {
            if data.last().copied() == Some(b'\0') {
                data.pop();
            }
            Ok(data)
        }
        ClipboardFormat::CF_UNICODETEXT => {
            let mut data = data.as_slice();
            let len = data.len();
            if len >= 2 {
                let clip = len - 2;
                if data[clip..] == [0, 0] {
                    data = &data[..clip];
                }
            }

            let units: Vec<u16> = data
                .chunks_exact(2)
                .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
                .collect();

            Ok(String::from_utf16_lossy(&units).into_bytes())
        }
        _ => Err(try_error(
            "attempted to decode unsupported clipboard format",
        )),
    }
}

/// Represents the CLIPRDR_SHORT_FORMAT_NAME structure.
#[derive(Debug)]
struct ShortFormatName {
    format_id: u32,
    format_name: [u8; 32],
}

#[allow(dead_code)]
impl ShortFormatName {
    fn id(id: u32) -> Self {
        Self {
            format_id: id,
            format_name: [0u8; 32],
        }
    }

    fn from_str(id: u32, name: &str) -> RdpResult<Self> {
        if name.len() > 32 {
            return Err(invalid_data_error(
                format!("{} is too long for short format name", name).as_str(),
            ));
        }
        let mut dest = [0u8; 32];
        dest[..name.len()].copy_from_slice(name.as_bytes());
        Ok(Self {
            format_id: id,
            format_name: dest,
        })
    }
}

impl FormatName for ShortFormatName {
    fn encode(&self) -> RdpResult<Message> {
        let mut w = Vec::new();
        w.write_u32::<LittleEndian>(self.format_id)?;
        w.write_all(&self.format_name)?;

        Ok(w)
    }

    fn decode(payload: &mut Payload) -> RdpResult<Self> {
        let format_id = payload.read_u32::<LittleEndian>()?;
        let mut format_name = [0u8; 32];
        payload.read_exact(&mut format_name)?;

        Ok(Self {
            format_id,
            format_name,
        })
    }
}

/// Represents the CLIPRDR_LONG_FORMAT_NAMES structure.
#[derive(Debug)]
struct LongFormatName {
    format_id: u32,
    format_name: Option<String>,
}

impl LongFormatName {
    fn id(id: u32) -> Self {
        Self {
            format_id: id,
            format_name: None,
        }
    }
}

impl FormatName for LongFormatName {
    fn encode(&self) -> RdpResult<Message> {
        let mut w = Vec::new();
        w.write_u32::<LittleEndian>(self.format_id)?;
        match &self.format_name {
            // not all clipboard formats have a name; in such cases, the name
            // must be encoded as a single Unicode null character (two zero bytes)
            None => w.write_u16::<LittleEndian>(0)?,
            Some(name) => {
                w.append(&mut util::to_unicode(name, true));
            }
        };

        Ok(w)
    }

    fn decode(payload: &mut Payload) -> RdpResult<Self> {
        let format_id = payload.read_u32::<LittleEndian>()?;
        let mut consumed = 0;
        let name: String = std::char::decode_utf16(
            payload
                .get_ref()
                .chunks_exact(2)
                .skip(payload.position() as usize / 2) // skip over format_id
                .take_while(|c| {
                    consumed += 2;
                    !matches!(c, [0x00, 0x00])
                })
                .map(|c| u16::from_le_bytes([c[0], c[1]])),
        )
        .map(|c| c.unwrap_or(std::char::REPLACEMENT_CHARACTER))
        .collect();

        // advance cursor in case there are more names to decode
        payload.set_position(payload.position() + consumed);

        Ok(Self {
            format_id,
            format_name: if name.is_empty() { None } else { Some(name) },
        })
    }
}

/// All data copied to a system clipboard has to conform to a format
/// specification. These formats are identified by unique numeric IDs,
/// which are OS-specific.
///
/// See section 1.3.1.2.
///
/// Standard clipboard formats are listed here: https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
///
/// Applications can define their own clipboard formats as well.
#[allow(dead_code, non_camel_case_types)]
#[repr(u32)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)]
enum ClipboardFormat {
    CF_TEXT = 1,         // CRLF line endings, null-terminated
    CF_BITMAP = 2,       // HBITMAP handle
    CF_METAFILEPICT = 3, // 1.3.1.1.3
    CF_SYLK = 4,         // Microsoft symbolik link format
    CF_DIF = 5,          // Software Arts' Data Interchange Format
    CF_TIFF = 6,         // tagged-image file format
    CF_OEMTEXT = 7,      // OEM charset, CRLF line endings, null-terminated
    CF_DIB = 8,          // BITMAPINFO
    CF_PALETTE = 9,      // 1.3.1.1.2
    CF_PENDATA = 10,     // Microsoft Windows for Pen Computing
    CF_RIFF = 11,        // audio data more complex than CF_WAVE
    CF_WAVE = 12,        // audio data in standard wav format
    CF_UNICODETEXT = 13, // unicode text, lines end with CRLF, null-terminated
    CF_ENHMETAFILE = 14, // handle to an enhanced metafile
    CF_HDROP = 15,       // identifies a list of files
    CF_LOCALE = 16,      // locale identifier, so application can lookup charset when pasting

    CF_PRIVATEFIRST = 0x0200, // range for private clipboard formats
    CF_PRIVATELAST = 0x02FF, // https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats#private-clipboard-formats

    CF_GDIOBJFIRST = 0x0300, // range for application-defined GDI object formats
    CF_GDIOBJLAST = 0x03FF, // https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats#private-clipboard-formats
}

/// Sent as a reply to the format list PDU - used to indicate whether
/// the format list PDU was processed succesfully.
#[derive(Debug)]
struct FormatListResponsePDU {
    // empty, the only information needed is the flags in the header
}

/// Sent by the recipient of the format list PDU  in order to request
/// the data for one of the clipboard formats that was listed in the
/// format list PDU.
///
/// See section 2.2.5.1: CLIPRDR_FORMAT_DATA_REQUEST
#[derive(Debug)]
struct FormatDataRequestPDU {
    format_id: u32,
}

impl FormatDataRequestPDU {
    fn for_id(format_id: u32) -> Self {
        Self { format_id }
    }

    fn encode(&self) -> RdpResult<Message> {
        let mut w = Vec::with_capacity(4);
        w.write_u32::<LittleEndian>(self.format_id)?;
        Ok(w)
    }

    fn decode(payload: &mut Payload) -> RdpResult<Self> {
        Ok(Self {
            format_id: payload.read_u32::<LittleEndian>()?,
        })
    }
}

/// Sent as a reply to the format data request PDU, and is used for both:
/// 1. Indicating that the processing of the request was succesful, and
/// 2. Sending the contents of the requested clipboard data
#[derive(Debug)]
struct FormatDataResponsePDU {
    data: Vec<u8>,
}

impl FormatDataResponsePDU {
    fn encode(&self) -> RdpResult<Message> {
        Ok(self.data.clone())
    }

    fn decode(payload: &mut Payload, length: u32) -> RdpResult<Self> {
        let mut data = vec![0; length as usize];
        payload.read_exact(data.as_mut_slice())?;

        Ok(Self { data })
    }
}

#[cfg(test)]
mod tests {
    use crate::vchan::ChannelPDUFlags;

    use super::*;
    use std::io::Cursor;
    use std::sync::mpsc::channel;

    #[test]
    fn decode_clipboard_overflow() {
        // a single byte is invalid for CF_UNICODETEXT
        let result = decode_clipboard(vec![54u8], ClipboardFormat::CF_UNICODETEXT).unwrap();
        assert!(result.is_empty());
    }

    #[test]
    fn encode_format_list_short() {
        let client = Client::default();
        let msg = client
            .add_headers_and_chunkify(
                ClipboardPDUType::CB_FORMAT_LIST,
                FormatListPDU {
                    format_names: vec![ShortFormatName::id(ClipboardFormat::CF_TEXT as u32)],
                }
                .encode()
                .unwrap(),
            )
            .unwrap();

        assert_eq!(
            msg[0],
            vec![
                // virtual channel header
                0x2C, 0x00, 0x00, 0x00, // length (44 bytes)
                0x13, 0x00, 0x00, 0x00, // flags (first + last + show protocol)
                // Clipboard PDU Header
                0x02, 0x00, // message type
                0x00, 0x00, // message flags (CB_ASCII_NAMES not set)
                0x24, 0x00, 0x00, 0x00, // message length (36 bytes after header)
                // Format List PDU starts here
                0x01, 0x00, 0x00, 0x00, // format ID (CF_TEXT)
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // format name (bytes 1-8)
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // format name (bytes 9-16)
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // format name (bytes 17-24)
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // format name (bytes 25-32)
            ]
        );
    }

    #[test]
    fn encode_format_list_long() {
        let empty = FormatListPDU::<LongFormatName> {
            format_names: vec![LongFormatName::id(0)],
        };

        let client = Client::default();

        let encoded = client
            .add_headers_and_chunkify(ClipboardPDUType::CB_FORMAT_LIST, empty.encode().unwrap())
            .unwrap();

        assert_eq!(
            encoded[0],
            vec![
                0x0e, 0x00, 0x00, 0x00, // message length (14 bytes)
                0x13, 0x00, 0x00, 0x00, // flags (first + last + show protocol)
                0x02, 0x00, 0x00, 0x00, // message type (format list), and flags (0)
                0x06, 0x00, 0x00, 0x00, // message length (6 bytes)
                0x00, 0x00, 0x00, 0x00, // format id 0
                0x00, 0x00 // null terminator
            ]
        );
    }

    #[test]
    fn encode_clipboard_capabilities() {
        let msg = ClipboardCapabilitiesPDU {
            general: Some(GeneralClipboardCapabilitySet {
                version: CB_CAPS_VERSION_2,
                flags: ClipboardGeneralCapabilityFlags::from_bits_truncate(0),
            }),
        }
        .encode()
        .unwrap();

        assert_eq!(
            msg,
            vec![
                0x01, 0x00, 0x00, 0x00, // count, pad
                0x01, 0x00, 0x0C, 0x00, // type, length
                0x02, 0x00, 0x00, 0x00, // version (2)
                0x00, 0x00, 0x00, 0x00, // flags (0)
            ]
        )
    }

    #[test]
    fn decode_clipboard_capabilities() {
        let msg = ClipboardCapabilitiesPDU::decode(&mut Cursor::new(vec![
            0x01, 0x00, 0x00, 0x00, // count, pad
            0x01, 0x00, 0x0C, 0x00, // type, length
            0x02, 0x00, 0x00, 0x00, // version (2)
            0x00, 0x00, 0x00, 0x00, // flags (0)
        ]))
        .unwrap();

        let general_set = msg.general.unwrap();
        assert_eq!(general_set.flags.bits(), 0);
        assert_eq!(general_set.version, CB_CAPS_VERSION_2);
    }

    #[test]
    fn decode_format_list_long() {
        let no_name = vec![0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
        let l = no_name.len();
        let decoded =
            FormatListPDU::<LongFormatName>::decode(&mut Cursor::new(no_name), l as u32).unwrap();
        assert_eq!(decoded.format_names.len(), 1);
        assert_eq!(
            decoded.format_names[0].format_id,
            ClipboardFormat::CF_TEXT as u32
        );
        assert_eq!(decoded.format_names[0].format_name, None);

        let one_name = vec![
            0x01, 0x00, 0x00, 0x00, // CF_TEXT
            0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, // "test"
            0x00, 0x00, // null terminator
        ];
        let l = one_name.len();
        let decoded =
            FormatListPDU::<LongFormatName>::decode(&mut Cursor::new(one_name), l as u32).unwrap();
        assert_eq!(decoded.format_names.len(), 1);
        assert_eq!(
            decoded.format_names[0].format_id,
            ClipboardFormat::CF_TEXT as u32
        );
        assert_eq!(
            decoded.format_names[0].format_name,
            Some(String::from("test"))
        );

        let two_names = vec![
            0x01, 0x00, 0x00, 0x00, // CF_TEXT
            0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, // "test"
            0x00, 0x00, // null terminator
            0x01, 0x00, 0x00, 0x00, // CF_TEXT
            0x74, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x65, 0x00, // "tele"
            0x70, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x74, 0x00, // "port"
            0x00, 0x00, // null terminator
        ];
        let l = two_names.len();
        let decoded =
            FormatListPDU::<LongFormatName>::decode(&mut Cursor::new(two_names), l as u32).unwrap();
        assert_eq!(decoded.format_names.len(), 2);
        assert_eq!(
            decoded.format_names[0].format_id,
            ClipboardFormat::CF_TEXT as u32
        );
        assert_eq!(
            decoded.format_names[0].format_name,
            Some(String::from("test"))
        );
        assert_eq!(
            decoded.format_names[1].format_id,
            ClipboardFormat::CF_TEXT as u32
        );
        assert_eq!(
            decoded.format_names[1].format_name,
            Some(String::from("teleport"))
        );
    }

    #[test]
    fn responds_to_monitor_ready() {
        let c: Client = Default::default();
        let responses = c
            .handle_monitor_ready(&mut Cursor::new(Vec::new()))
            .unwrap();
        assert_eq!(2, responses.len());

        // First response - our client capabilities:
        let mut payload = Cursor::new(responses[0].clone());
        let _pdu_header = vchan::ChannelPDUHeader::decode(&mut payload).unwrap();
        let header = ClipboardPDUHeader::decode(&mut payload).unwrap();
        assert_eq!(header.msg_type, ClipboardPDUType::CB_CLIP_CAPS);

        let capabilities = ClipboardCapabilitiesPDU::decode(&mut payload).unwrap();
        let general = capabilities.general.unwrap();
        assert_eq!(
            general.flags,
            ClipboardGeneralCapabilityFlags::CB_USE_LONG_FORMAT_NAMES
        );

        // Second response - the format list PDU:
        let mut payload = Cursor::new(responses[1].clone());
        let _pdu_header = vchan::ChannelPDUHeader::decode(&mut payload).unwrap();
        let header = ClipboardPDUHeader::decode(&mut payload).unwrap();
        assert_eq!(header.msg_type, ClipboardPDUType::CB_FORMAT_LIST);
        assert_eq!(header.msg_flags.bits(), 0);
        assert_eq!(header.data_len, 6);

        let format_list =
            FormatListPDU::<LongFormatName>::decode(&mut payload, header.data_len).unwrap();
        assert_eq!(format_list.format_names.len(), 1);
        assert_eq!(format_list.format_names[0].format_id, 0);
        assert_eq!(format_list.format_names[0].format_name, None);
    }

    #[test]
    fn encodes_large_format_data_response() {
        let mut data = Vec::new();
        data.resize(vchan::CHANNEL_CHUNK_LEGNTH + 2, 0);
        for (i, item) in data.iter_mut().enumerate() {
            *item = (i % 256) as u8;
        }
        let pdu = FormatDataResponsePDU { data };
        let encoded = pdu.encode().unwrap();
        let client = Client::default();
        let messages = client
            .add_headers_and_chunkify(ClipboardPDUType::CB_FORMAT_DATA_RESPONSE, encoded)
            .unwrap();
        assert_eq!(2, messages.len());

        let header0 =
            vchan::ChannelPDUHeader::decode(&mut Cursor::new(messages[0].clone())).unwrap();
        assert_eq!(
            ChannelPDUFlags::CHANNEL_FLAG_FIRST | ChannelPDUFlags::CHANNEL_FLAG_SHOW_PROTOCOL,
            header0.flags
        );
        let header1 =
            vchan::ChannelPDUHeader::decode(&mut Cursor::new(messages[1].clone())).unwrap();
        assert_eq!(
            ChannelPDUFlags::CHANNEL_FLAG_LAST | ChannelPDUFlags::CHANNEL_FLAG_SHOW_PROTOCOL,
            header1.flags
        );
    }

    #[test]
    fn responds_to_format_data_request_hasdata() {
        // a null-terminated utf-16 string, represented as a Vec<u8>
        let test_data = util::to_unicode("test", true);

        let mut c: Client = Default::default();
        c.clipboard
            .insert(ClipboardFormat::CF_UNICODETEXT as u32, test_data.clone());

        let req = FormatDataRequestPDU::for_id(ClipboardFormat::CF_UNICODETEXT as u32);
        let responses = c
            .handle_format_data_request(&mut Cursor::new(req.encode().unwrap()))
            .unwrap();

        // expect one FormatDataResponsePDU
        assert_eq!(responses.len(), 1);
        let mut payload = Cursor::new(responses[0].clone());
        let _pdu_header = vchan::ChannelPDUHeader::decode(&mut payload).unwrap();
        let header = ClipboardPDUHeader::decode(&mut payload).unwrap();
        assert_eq!(header.msg_type, ClipboardPDUType::CB_FORMAT_DATA_RESPONSE);
        assert_eq!(header.msg_flags, ClipboardHeaderFlags::CB_RESPONSE_OK);
        assert_eq!(header.data_len, 10);
        let resp = FormatDataResponsePDU::decode(&mut payload, header.data_len).unwrap();
        assert_eq!(resp.data, test_data);
    }

    #[test]
    fn invokes_callback_with_clipboard_data() {
        let (send, recv) = channel();

        let mut c = Client::new(Box::new(move |vec| {
            send.send(vec).unwrap();
            Ok(())
        }));

        let data_format_list = FormatListPDU {
            format_names: vec![LongFormatName {
                format_id: ClipboardFormat::CF_TEXT as u32,
                format_name: None,
            }],
        }
        .encode()
        .unwrap();

        let data_resp = FormatDataResponsePDU {
            data: String::from("abc\0").into_bytes(),
        }
        .encode()
        .unwrap();

        let mut len = data_format_list.len() as u32;
        c.handle_format_list(&mut Cursor::new(data_format_list), len)
            .unwrap();

        len = data_resp.len() as u32;
        c.handle_format_data_response(&mut Cursor::new(data_resp), len)
            .unwrap();

        // ensure that the null terminator was trimmed
        let received = recv.try_recv().unwrap();
        assert_eq!(received, String::from("abc").into_bytes());
    }

    #[test]
    fn update_clipboard_returns_format_list_pdu() {
        let mut c: Client = Default::default();
        let messages = c.update_clipboard("abc".to_owned()).unwrap();
        let bytes = messages[0].clone();

        // verify that it returns a properly encoded format list PDU
        let mut payload = Cursor::new(bytes);
        let _pdu_header = vchan::ChannelPDUHeader::decode(&mut payload).unwrap();
        let header = ClipboardPDUHeader::decode(&mut payload).unwrap();
        let format_list =
            FormatListPDU::<LongFormatName>::decode(&mut payload, header.data_len as u32).unwrap();
        assert_eq!(ClipboardPDUType::CB_FORMAT_LIST, header.msg_type);
        assert_eq!(1, format_list.format_names.len());
        assert_eq!(
            ClipboardFormat::CF_TEXT as u32,
            format_list.format_names[0].format_id
        );

        // verify that the clipboard data is now cached
        // (with a null-terminating character)
        assert_eq!(
            String::from("abc\0").into_bytes(),
            *c.clipboard.get(&(ClipboardFormat::CF_TEXT as u32)).unwrap()
        );
    }

    #[test]
    fn update_clipboard_conversion() {
        struct Item(&'static str, &'static [u8], ClipboardFormat);
        for Item(input, expected, format) in [
            Item("abc\0", b"abc\0", ClipboardFormat::CF_TEXT), // already null-terminated, no conversion necessary
            Item("\n123", b"\r\n123\0", ClipboardFormat::CF_TEXT), // starts with LF
            Item("def\r\n", b"def\r\n\0", ClipboardFormat::CF_TEXT), // already CRLF, no conversion necessary
            Item("gh\r\nij\nk", b"gh\r\nij\r\nk\0", ClipboardFormat::CF_TEXT), // mixture of both
            Item(
                "🤑\n",
                &[62, 216, 17, 221, b'\r', 0, b'\n', 0, 0, 0],
                ClipboardFormat::CF_UNICODETEXT,
            ), // detection and utf8 -> utf16 conversion & CRLF conversion
            Item(
                "🤑\r\n",
                &[62, 216, 17, 221, b'\r', 0, b'\n', 0, 0, 0],
                ClipboardFormat::CF_UNICODETEXT,
            ), // detection and utf8 -> utf16 conversion & no CRLF conversion
        ] {
            let mut c: Client = Default::default();
            c.update_clipboard(input.to_owned()).unwrap();

            assert_eq!(
                expected,
                *c.clipboard.get(&(format as u32)).unwrap(),
                "testing {}",
                input,
            );
        }
    }
}


text:
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
    <entity name="App\Entity\PaymentType" table="payment_type"
            repository-class="App\Repository\PaymentTypeRepository">
        <id name="id" type="integer" column="id">
            <generator strategy="AUTO"/>
        </id>

        <field name="days" column="days" type="integer" nullable="true"/>
        <field name="endOfMonth" column="end_of_month" type="boolean"/>
        <field name="position" column="position" type="integer"/>
    </entity>
</doctrine-mapping>

redpajama_set_name: RedPajamaGithub


Might be helpful to me/my family.
I already have university/college training in research.
Need more info about this idea to determine if it would help me (please try to post any questions you may have on forum as well).
Your post was too long. I lost interest midway, so I don't know.
Grad student here...(Never involved in CCSVI or any medication research). I have always been a strong proponent of solid research and established evidence guiding clinical care (in any condition) bc I have witnessed and read about how failing to properly interpret research/evidence can lead to detrimental effects on patients.
I have been looking into the views/forums of MS community and media reports on CCSVI. Most of you will agree that this issue has become controversial bc sometimes doctors, patients and media misrepresent the true results out there and sometimes, studies are misinterpreted. e.g., some conclude that CCSVI has already been established as a full "cure" and others have concluded that CCSVI has no basis/is not the answer/is too risky without having all the facts right. Both are clearly incorrect. Part of the issue is that it is difficult for us to determine what is/is not reliable, and how to interpret things we read (I'll be the first to admit that, since it took several years of training to begin to accurately appraise studies without letting my personal views get in the way).
I have been pondering the CCSVI issue for weeks. I would like to gauge your interests/opinions on a live interactive "talk" intended for MS patients (and perhaps other conditions)... THE GOALS OF THE "TALK" BEING: (1) scientific evidence - what/why...etc., (2) what to look for when reading studies or article or personal stories, (3) how to detect reliable info especially about new potential treatments/CCSVI, (4) how to avoid common pitfalls in interpretation, (5) how to easily put together the wealth of info out there to formulate ur own decision about treatment/etc.
All this may sound simple enough to read/interpret studies/articles, but ask any scientist, there's sooo much to it that we don't know till you get into it; even docs misinterpret their work occasionally!
WHAT THIS TALK WILL NOT BE ABOUT: (1) anyone's personal opinions (presenter's, docs, other patients, media...etc.) because that is the opposite of objective science, (2) telling ppl what to do for their treatments (I believe we all have absolute right over our bodies and have to make our own choices - but must be able to get accurate info), (3) changing people's opinions about anything related to their health - only looking at how knowledge about things is obtained and what it means.
I thought this would be ideal for MS patients who are always trying to be up to date on their condition... more so than any other patient groups I've met... the only issue being that there's way too much info/misinfo out there about MS nowadays.
I believe that all people have the right to accurate information when it comes to their care and treatment. If they don't trust their docs or societies for now, then they deserve a way to explore the topics reliably on their own. So I thought it would help make them more comfortable with this, without having to spend years in a research program!
Obviously, the details are still to be worked out. But for now, PLEASE PLEASE PLEASE reply with your opinions in the thread... You are the first beings out there that I told about this idea - cause ultimately, it's your opinion that matters in this issue. Please give any constructive feedback, either way, and preferably anything about this idea that needs improvement/change or what else you may find helpful. (please also vote on the poll if you don't mind).
THANK YOU SO MUCH! YOUR GUIDANCE IS MUCH APPRECIATED!
Rather than a talk, which might require being online at a set time (which is difficult as a mother and as a pwMS, I grab time as I can), why not post some of what you know right here in this thread? Or film a presentation and put it up on youtube? I think you might get further with low-commitment ideas such as these ones (commitment on our part, that is, of the time it would take to tune in for a talk and being there at the right scheduled time).
What you've said sounds interesting so far.
Is this idea kinda like a Snopes.com for research? If that's the case I love it.
Thank you for your helpful comments. And thanks to those who voted.
Cece, thank you for the welcome. Both you and civickiller are right - whatever this turns into, it should be more accessible to everyone regardless of their schedules. I was actually thinking of someone with more authority, who has been in scientific field for years - we have a med professor who has devoted his life and has pioneered "evidence-based medicine" that is taking over the field! Posting stuff in writing will be like any other blog... using visual aids and examples from medicine in history and MS in a video format I would think will be more effective.
Shueywho, thanks for the site - I had never heard of it but it seems very interesting after I checked it out! The difference with what I'm thinking of is that the while the site gives accurate info (from what I saw), their explanations are still not based on evidence - they don't give us the numbers/ studies/evidence. They just state things. Giving people the tools to interpret basic information on their own would be truly practical in my opinion.
Any further comments/votes would be greatly appreciated as I try to figure out! Thanks again!
But vitamin D can also be sourced from fish oil.


text:
How to Use Dashes
There are two kinds of dashes you're likely to need in your writing—em dashes and en dashes. Neither is the same as a hyphen, so don't think you can substitute without anyone noticing. An en dash is traditionally the length of a letter n, while em dashes are—you guessed it—the length of an M.
- hyphen
– en dash
Cats make great roommates—if you don't mind the smoking—and terrible landlords.
The Em Dash—An Interruption
The em dash is what most of us think of when we think of dashes—a break in the flow of the sentence. It's often used as a colon, to introduce or expand on what came before (see Colon vs. Semicolon for more on colons), as in the last sentence and in this section's heading.
Em dashes commonly work in pairs to set off a word or phrase from the rest of the sentence. They're a little more emphatic than a pair of commas but a little less off-topic than parentheses.
In dialogue, a pair of em dashes can make room for an action, and a single em dash can show interrupted or faltering speech. (For more examples, see How to Punctuate Dialogue.)
"He said he'd be walking the thylacine"—Shareena checked her phone—"right about now."
"Whatever you do, don't push that—"
Occasionally, an em dash follows a word or words that introduce a sentence's main clause.
Revenge—that was the sisters' goal.
Aliens, spaceships, face-eating fungi—nothing fazed Felipe.
Em dashes are often put before the source of a quotation, though they are not obligatory.
If you can't say anything nice, come sit by me.
The Dash With Other Punctuation
When a comma, semicolon, or period falls next to a dash, the dash shoulders it off the page. But when a pair of dashes encloses a phrase ending in an exclamation point or question mark, that punctuation can stay.
Your combat skills are inferior—at least, inferior to mine—but your fashion sense is unrivalled. (No comma before but.)
Spade unwrapped a black bird—could it be the Maltese falcon?—and casually set it on his desk.
It's not like I have a crush on him—as if!—but he does have nice hair.
The En Dash
En dashes are used to express ranges, where you might otherwise write from…to… or between…and…. They can also be used instead of to.
The average human heart weighs 250–300 grams.
Randy's Roller Disco is open Tuesday–Sunday.
Eartha Kitt played Catwoman in the 1967–68 season of Batman.
The Rome–Sydney flight was cursed by a jet-lagged strega.
Our impromptu game of "flaming quidditch" ended in a score of 7–3 and a visit to the ER.
What you don't want to do is combine en dashes with from or between. Use words or dashes, not both.
The Countess plans to travel incognito from April 6–May 12. X
Emile and his imps serve coffee daily from 7:00 a.m. to 4:00 p.m. ✓
Don't go near the castle August–October. ✓
Mastering spatula combat can take between 3–4 years. X
Improving your diction will require between 10 and 15 marbles. ✓
Each morning I believe 5–7 impossible things before breakfast. ✓
Dashes, Typography, and Spacing
The equivalent of an em dash in typescript--in case you're stranded on a desert island with only a typewriter--is two hyphens. As for en dashes, you can probably get away with using a hyphen instead—in fact, the Associated Press prefers it. However, there's no reason to be old-fashioned when you have a computer at your disposal. Programs like Word automatically transform two hyphens--without spaces--into an em dash and a single hyphen - with spaces - into an en dash. (More on this below.) Since computers can't always read your mind, it's good to know the keyboard shortcuts:
En dash: Alt + Hyphen
Em dash: Alt + Shift + Hyphen
Windows (use numeric keypad)
En dash: Alt + 0150
Em dash: Alt + 0151
Should you put spaces around your dashes? Most American style guides, including The Chicago Manual of Style, prefer closed em dashes—that is, em dashes without spaces. But many British publishers use open en dashes – that is, en dashes with spaces – instead, and The Elements of Typographic Style considers the open en dash aesthetically superior to the em dash.
Though the closed em dash is more widely used, both are correct, so the choice is up to you. However, don't put spaces around your en dashes when they are expressing ranges or to; such en dashes should always be closed.
Your combat skills are inferior—at least, inferior to mine—but your fashion sense is unrivalled.
A final word on dashes—try not to use too many. It may be tempting—they're so handy—but more than one or two per page—three at most—will look choppy and may distract your reader. I am, myself, occasionally guilty of overusing the dash. I have certainly done so in this post—purely for pedagogical purposes, of course.
Labels: Catwoman, closed em dash, commas, exclamation point, hyphens, open en dash, punctuation, question mark, quotations, spacing
for ict 99 3 February 2018 at 19:54

redpajama_set_name: RedPajamaCommonCrawl


Delivering up to 20,000 hours of maintenance-free operation, Hitachi Digital Media Group announces their first 8,000 lumen single-chip DLP Laser projector. The LP-WU9750B has been designed from the ground-up with a range of features to offer high reliability and stability to meet the needs of customers in demanding installation environments.
The quad-drive laser engine also offers near-instant 100% brightness and built-in redundancy in the event of an unlikely single laser failure. The advanced new cooling system and heat-resistant phosphor wheel ensure that the high performance of the laser goes hand-in-hand with reliable operation.
Advanced installation options include 360-degree projection and portrait mounting as well as edge blending and warping, with fine adjustment of brightness.
The LP-WU9750B offers 5 digital connection options including SDI, HDBaseT, DVI and two HDMI ports.
With seven interchangeable lenses, including the Ultra-Short Throw FL-920, providing options for screen sizes of between 100" to 600" and projection distance for the 100" screen ranging from 0.83m to 19m, the projector together with powered focus, shift and zoom can meet the specific demands of any environment.
For additional peace of mind, this projector offers a 3 year or 10,000 hours warranty, whichever comes first (for extended warranty please contact your local dealer).
As well as offering an impressive brightness level of 8000 lumens the LP-WU9750B also delivers an industry leading contrast ratio of 20,000:1.
The projector measures 500x216x576, weighs 28Kg and is available now from Midwich, Sahara Presentation Systems Plc and Medium UK.
Industry First: 8,000 Lumens, single-chip DLP Laser projector with quick start / quick off operation and contrast ratio of 20,000:1.
Maintenance-free Operation: laser light source delivers 20,000 hours of maintenance free operation.
Highly Reliable: A new quad-drive laser as well as a sealed, dust-resistant engine, a new heat-resistance phosphor wheel and a new cooling system.
Advanced Installation: Unlimited edge blending and warping, flexible mounting, P-in-P/P-by-P, five digital inputs and seven interchangeable lens options deliver infinite installation possibilities.
3 year/10,000 hours warranty: A 3 year or 10,000 hours warranty, whichever comes first.
The Digital Media Group, which is part of Hitachi Europe Ltd, is responsible for pan-European sales and distribution of Hitachi's leading digital display and projection products.
With two key product areas, Hitachi Digital Media Group provides leading-edge digital solutions for the home and business environments. Key product groups are display solutions, which include High Definition Ready LCD TVs, and a range of business and education 3LCD projectors.
Hitachi Europe Ltd., a subsidiary of Hitachi, Ltd., is headquartered in Maidenhead, UK. The company is focused on its Social Innovation Business - delivering innovations that answer society's challenges. Hitachi Europe and its subsidiary companies offers a broad range of information & telecommunication systems; rail systems, power and industrial systems; industrial components & equipment; automotive systems, financial services; digital media & consumer products and others with operations and research & development Laboratories across EMEA.
For more information about the company, please visit http://www.hitachi.eu.
Hitachi, Ltd. (TSE: 6501), headquartered in Tokyo, Japan, delivers innovations that answer society's challenges. The company's consolidated revenues for fiscal 2015 (ended March 31, 2016) totaled 10,034.3 billion yen ($88.7 billion). The Hitachi Group is a global leader in the Social Innovation Business, and it has approximately 335,000 employees worldwide. Through collaborative creation, Hitachi is providing solutions to customers in a broad range of sectors, including Power / Energy, Industry / Distribution / Water, Urban Development, and Finance / Government & Public / Healthcare.


Many Veterans filing a disability claim with VA simply fill in the 21-526EZ, Application for Disability Compensation and Related Compensation Benefits, list the disabilities they are filing a claim for, cross their fingers and hope for the best. Here are some suggestions to help you be successful with your claim the first time it's submitted.
Intent to file – It may take some time for you to gather the information to support your fully developed claim, such as private treatment records and written statements. You can hold your effective date by filing a VA Form 21-0966, Intent To File A Claim For Compensation and/or Pension, or Survivors Pension and/or DIC. The date this form is received by VA is used as your potential effective date for payment purposes. You have a year after it's received by VA to file your completed claim application.
Write a Statement in Support of Claim -When filing your claim, include a VA Form 21-4138, Statement in Support of Claim. Do a separate paragraph for each disability you are claiming, and explain the event in service (be specific) and your current disability or symptoms related to your injury or illness. Provide any evidence available on the event, such as personnel records, award narrative, and medical records .
Compensation and Pension (C&P) Exam.- Even if you submit all of your medical records, you may be scheduled for a C&P exam. This is not a typical doctor's exam, and in some cases, the doctor may just review your records – including any statements in your file – and ask you a few questions. While this may seem unusual for an exam, the doctor is actually filling in a Disability Benefits Questionnaire (DBQ), which the rater will use to determine if your claim can be granted, and at what percentage. Some information for the form will come from your medical records, and additional information is gathered from you. Failure to have this examination may result in an immediate disqualification of your claim.


We are excited to share our brand new In-Stock Therma-Tru catalog! This catalog was designed to best serve you and help guide the selling process. It is available now at reeb.com/catalogs and our printed versions will be available in the next weeks.
We included educational information on choosing a door, glass types available, door unit components, and finishing options. The quick guide at the front provides an up-close look at each grain type available, which layouts you can get in that grain, and the glass types available. The main portion of the catalog is organized by glass type in alphabetical order and showcases all the door styles Reeb stocks.
View the screencast below to get a closer look at the catalog and then check it out for yourself!


Waterlilies float in a calm pond on a clear day in this painting. I found a lovely quiet lily pond in Sonoma at the Sunset test gardens there. I thought about the wonderful paintings of waterlilies that Monet painted, and of his delightful pond at Giverny.


text:
PS4 and XBOX One Games Releasing in March (2015)
EditorialsNewsPlayStation
From weird little shooters to survival horror strategy and adventure RPG's, March of 2015 is shaping up to be an interesting month for current (8th) generation gamers. This isn't just a notable period for PlayStation 4 gamers though, it is also just as equally appealing for Microsoft and the Xbox One crowd. Anyway, without further ado, let's explore some of the titles releasing in March for both consoles…
Resident Evil Revelations 2 – Episode 1,2,3,4
OlliOlli 2: Welcome to OlliWood
Helldivers
Zombie Army Trilogy
Hotline Miami 2: Wrong Number
Bladestorm: Nightmare
DmC: Devil May Cry – Definitive Edition
Borderlands: The Handsome Collection
Screamride
Forza Horizon 2 Presents Fast & Furious
Out on PS4 and XBOX One March 3rd.
Artsy black and white noir-esque visuals, puzzle-solving, an old mansion… what's not to like? Needless to say the game's art style and emphasis on the dichotomy between b&w graphics plays a pivotal role here with regards to gameplay riddles. It seems to offer familiar themes inside of a slightly new approach and seems poised to delight fans in search of "old school" survival horror action and drama.
Out on PS4 and XBOX One March. Episode 1 released in February.
Taking the series in a somewhat familiar but new direction, Resident Evil Revelations 2 sees the franchise cashing in on the recent boon of "installment mania". Basically, this means breaking up what could be a single lengthy title into a number of pieces, thus allowing for a different mode of development as well as audience experience. Episode 1 was met with mixed but largely positive reviews and seems to have set the pace for what's to come next. By and large, the cooperative action seems to be where the real fun is, so keep that in mind if you love survival horror and have lots of friends to play with.
Out on XBOX One March 2015. Already released on PS4.
Some might be surprised that a bona-fide sequel is already coming out for OlliOlli, but that's the way it goes, so deal with it. It is basically an exciting, sort of retro-looking (in only a few ways) side-scrolling skating game that features all sorts of exciting action as well as an engrossing soundtrack. Naturally, in addition to making its way onto the PlayStation 4 it's also going to find a home on the Vita as well.
Out on PS4 March 3rd.
This top-down, procedurally-generated game features alluring sci-fi shooting action which is meant to engage an entire community… Visually it is sort of similar to what you find in Diablo III, just in terms of style, of course; graphically and art-wise, it is certainly original and able to stand on its own merits. Helldivers is a neat choice for those who like a more arcade-ish experience. ..
Out on PS4 and XBOX One March 6th.
Simply put: an extremely violent and bloody third-person tactical shooter which affords a sizable amount of content including online play. Essentially it is an excuse to fuel the market for games featuring loose Nazi memorabilia and the subsequent killing of decrepit zombies therein. Is if fun? Well, it certainly seems that way…
Out on PS4 March 10th.
This 2-D series has remained viable because of its relentless adherence to its rather retro format and controversy surrounding its propensity for in-game violence. Needless to say, this new addition which drops near mid-March is set to give fans even more of what they have been waiting for.
Out on PS4 and XBOX One March 17th.
Turning away from traditional war environments and becoming domestic, Battlefield Hardline is set to be a pretty rapid departure for the well-known FPS combat-centric series. Don't worry though; the same basic formula is still at play here, which includes a gripping single player campaign as well as loads of options for multiplayer. Obviously, the focus is on police duties and the various dramatic narratives that go along with fighting crime, be it outwardly or through internal corruption.
Bladestorm: Nightmare is a real-time tactics experience that blends fantasy with history to entice gamers. Fans will be understandably excited about this release as it contains a lot of content and a few notable twists and turns.
Anyone who considers themselves to be a fan of hack and slash action, not to mention the DmC series in general, should be pretty excited about the release of the "Definitive Edition". To get straight to the point: it features a rebuild of the original game with all sorts of new additions and so much content that it is ridiculous.
This HD version of FF Type-0 is poised to delight PS4 owners with its merging of modern military apocalyptic and fantasy themes. Definitely a bold direction for the series, yet it retains the classic style we all know and love at the same time.
From Software is bringing us a whole new IP here that sort of acts as a spiritual successor to their ever-popular Dark Souls series. You're going to get more challenging third-person gameplay here too, with amazing visuals and steeped in a dark, macabre feel.
First-person action combined with RPG mechanics and an amazing loot-collecting system…that's what the borderlands series has always been about. With Borderlands: The Handsome Collection you get twice the fun with Borderlands 2 bundled with Borderlands: the Pre-sequel.
Out on XBOX One March 6th.
If you're a fan of construction and management simulators then Screamride is going to be right down your alley. First off, it is the successor to RollerCoaster Tycoon, a legendary game in its own right. It promises great graphics, 50 levels and no less than three different gameplay modes.
Out on XBOX One March 11th.
Ori and The Blind Forest is an atmospheric and engrossing platformer which features a naturalistic, somewhat spiritual narrative. You collect experience and add new abilities along the way as you progress.
Out on XBOX One March.
As its name implies, this is an addition to Forza Horizon 2 which is meant to tie-in with the popular auto-centric movie series "Fast and Furious". Whether or not it is going to be a hit is uncertain, but it is a great idea and is also going to be made freely available (between March 27th and April 10th), so don't miss out.
GamerBolt Team - December 26, 2020
When you're gaming, having access to the right equipment is key. After all, if you're halfway through a raid or about to make a...
The new generation of consoles is here, and it's very flashy and impressive, of course. The PlayStation 5 and Xbox Series X (and its...
The new Xbox Series X is a truly gorgeous console. It's a huge beast of a machine, boasting a monolith-style design that shows off...
Classic Sega Games We Should Never Forget
Sega games are classic form of gaming that have been around for years. Although many believe they had their moment in the early 2000's,...


The Federation of Saskatchewan Indian Nations (FSIN) is a Saskatchewan -based First Nations organization.
The federation grew out of a number of different organizations. Although the Federation of Saskatchewan Indians (FSI) was organized in 1959 (with John Tootoosis as its first president), it grew out of the Union of Saskatchewan Indians, founded February 14, 1946. The Union was created with the merger of two other groups, the Protective Association for the Indians and their Treaties, and the Association of Saskatchewan Indians, (officially incorporated in 1945 with Joe Dreaver as president.
On April 16, 1982, FSI restructured itself into a true federation, and changed its name to the current Federation of Saskatchewan Indian Nations.


Are you looking for 85 acres of land to develop? Perfect for commercial endeavors such as shopping center, trailer park, camp grounds, housing development, solar project , cell phone tower. Many, many uses. Zoned commercial and residential. Please contact LB for additional information and zoning regulations for this land. Where are you going to find 85 acres of land in Massachusetts? Egress is off 148 Lafayette St. The front 25+acres has been to Land Court. The rear 60+ acres has been surveyed, staked and flagged. Your work has been done for you. Just bring your plans and ideas.
BEST MULTI UNIT OPPORTUNITY AT SALISBURY BEACH. TOTAL OF 5 UNITS. DIRECT OCEANFRONT 3 FAMILY OFFERING UNOBSTRUCTED SPECTACULAR VIEWS OF THE ATLANTIC.PLUS OCEANSIDE 2 FAMILY. NORTH END LOCATION ONE MILE FROM SEABROOK BEACH LINE. EACH UNIT HAS 3 BEDROOMS, 1 BATH, DECKS AND PORCHES. 3 STALL GARAGE. SEPARATE UTILITIES. 3 UNITS ARE YEAR ROUND. CURRENT GROSS INCOME APPROXIMATELY $90,000.00 ALSO, GOOD OPPORTUNITY FOR POSSIBLE CONDO CONVERSION PROJECT.
RARE OPPORTUNITY FOR JOINT PROJECT! Duplex Lot. This property is being offered as a larger lot with frontage on both North End Blvd AND on Commonwealth Ave OR can be 2 separate lots with 50 ft of frontage on each. The larger lot is approximately 7,012 sq ft. Build a new duplex home (on pilings) and share expenses with parking available off of both streets. See MLS listings #72378235 & #72378235 for separate lot purchase. Call listing agent for more information. Also see paper clip for pertinent documents. Steps to beach access and the Ocean. Height limit is 35 ft for building. THIS PRICE FOR 2 LOTS: 552 NORTH END BLVD & 42 COMMONWEALTH AVE. Flood Insurance is not required in this area of the beach..
Impressive colonial with its estate like setting, nestled on a beautiful country road. Conveniently located minutes to highways, beaches, shopping, schools and walking trail. Truly a unique spacious property offering a separate living area for guests or extended family members. Large great room, newly remodeled kitchen, a sun filled dining area, open concept, perfect for entertaining, laundry off kitchen. stunning Mantled fire place, Five large bedrooms, two oversize full bathrooms. Impressive deck overlooking beautiful in- ground pool, landscaped and enclosed back yard. Detached two car garages includes additional legal office space above garage for man town, pool house, workshop or office. A must see!!You will fall in love with this Salisbury stunner.
OCEANFRONT 4 BEDROOM COLONIAL LOCATED ON DESIRABLE NORTH BEACH HOME. FEATURES INCLUDE FIREPLACE LIVING ROOM WITH SLIDERS TO YEAR ROUND SUN PORCH, CENTRAL A/C, ELECTRIC SHADES, FAMILY ROOM, AND DINING ROOM ALL ON FIRST FLOOR. CENTRAL A/C THRU OUT 4 BEDROOMS ON 2ND FLOOR 2 BEDROOMS INCLUDE DECKS WITH FABULOUS OCEAN VIEWS 2 BEDROOMS WITH MARSH VIEWS. 4 CAR PARKING FULL BASEMENT .
OCEANFRONT SPACIOUS SINGLE FAMILY 5 BEDROOM COTTAGE, WELL MAINTAINED. PLENTY OF ROOM FOR FAMILY AND FRIENDS. FEATURES INCLUDE UPDATED KITCHEN, FIRE PLACED LIVING ROOM, LAMINATE FLOORING, 2 CAR PARKING, PRIVATE PATIO FOR FAMILY COOK-OUTS. DEEDED RIGHT OF WAY TO BEACH EASY WALK TO RESTAURANTS AND ENTERTAINMENT CENTER. ALL FURNITURE AND APPLIANCES TO REMAIN AS VIEWED LOCATED ON ATLANTIC AVE A DEAD END ONE WAY STREET ON SOUTH END OF SALISBURY BEACH.
Property has Yield Plan done. This plan shows 7 house lots. The back part of this lot has some wetlands which abuts the town of Salisbury property. There is a one story building on the property that most likely would be torn down. Please do not go on the property with out permission from listing agent.
MILES of beautiful, sandy beach in your FRONT YARD! Waterfront two bedroom, 2 bath condominium residence with elevator service directly to the unit. Amenities include gourmet granite kitchen with SS appliances, wonderful open floor plan and hardwood floors. A wall of glass offers million dollar views and opens to a private balcony where you can sit and enjoy the oceanside ambiance. This one won't last. Garage parking for 2 cars and small storage closet on ground level. Accompanied showings by appointment. Elevator opens directly to the unit. Bedroom Flooring has just been upgraded with new engineered hardwood.
Brand New Construction! - Craftsman-style Colonial!! 2,750 +/- sq. ft. w/4 bedrooms, 3 bath, 2-car over-sized garage in a convenient, commuter-friendly location. Enjoy all the luxuries of a brand new home including 9' 1st floor ceilings, Maple hardwood floors & custom features throughout this energy efficient home. Open concept living at it's best! Enter this home through a beautiful foyer which leads you to the great room centered by a custom-tiled gas fireplace.The open concept design continues from the great room to the kitchen & dining area w/sliders to a back deck overlooking a wooded lot. This massive room is anchored by the kitchen island & includes a large pantry, granite counter tops, SS appliances & custom cabinetry. A full bath & large mudroom w/access from the garage complete the 1st floor. 2nd floor includes master bedroom, 3 additional bedrooms, all w/walk-in closets, a laundry room, large linen closet & pull-down access to attic. Don't wait!
Contractors -Investors -Business Owners Take Notice! Opportunity awaits this Rare Find. Location is an Understatement for this 1.082 of Land and Diner nothing could be Finer! Located in the Heart of Village Commercial (VC) District of Salisbury Square. Many Permitted Uses such as: Agricultural, Bakery, Department Store, Laundromat, Nursery School, Professional and Business Office Building, Restaurant, Fast Food, Retail Store Selling Food, Drugs, and Proprietary Goods. Currently Owned and Operated as Pats Diner a Breakfast & Lunch -Family Diner since 1999. There is Profit to be made here! Sell the Diner its one of a kind Classic 1950's Worchester Lunch Car #824.Possibilities are Endless!
ENTERTAINER'S DREAM. This lovely expanded colonial in the Salisbury Plains awaits those seeking a large, impeccably maintained four bedroom home on a cul-de-sac with a large yard. Over 700 Sq. Ft of deck space with a cabana, outdoor bar, and outdoor fireplace. Inside this large home many updates including gleaming hard wood floors, maple kitchen cabinets with breakfast bar, pantry, new gas range and granite counter tops. There is also a very large living room, separate dining room, and enclosed family room with a brick wood burning fireplace. On the second floor there is a large sun filled master bedroom with a private full bath, a large bedroom with walk-in closet, 2 more bedrooms and another full bath. All appliances, 6 person hot tub and a portable generator included in the sale. New septic to be installed at sellers expense prior to closing. Over all beautiful, sun filled, warm and inviting home.
There is currently a 2 family home on the property. Seller has procured a variance to divide the one lot with frontage on both Commonwealth Ave and North End Blvd into 2 lots for 2 single family homes.. Water hookup is on site for Comm Ave site. Sewer hook-up is on North End Blvd, but there is a sewer main on Commonwealth Ave. Survey is in hand. Buyer to be responsible for all permits required by the Town of Salisbury for a single family home. This is also a great opportunity for 2 members of a family to have neighboring beach homes. 42 Comm Ave and 552 North End Blvd. House on the property needs to be a single family or taken down.. Location, Location!! Flood insurance is NOT required in this area of the beach.
A great investment opportunity! Property consists of 2 lots, one with a 2 bedroom ranch on an approximate 2,221 square foot lot and an adjacent vacant lot that is approximately 4,748 square feet. The ranch-style home is suited to both long and short term rentals. Current owner rents tenant-at-will for $1,300/mo.(tenant pays utilities).The combined properties are within reasonable proximity to the beach center and public beaches and are situated in the Beach Commercial Zone. Ideal for multi-unit development or other uses as permitted by zoning. Property is being sold "as is" - all permitting is the responsibility of the buyer.


Why should I take the pledge?
Do reps influence doctors' prescribing?
Do reps offer useful education for doctors?
Aren't reps simply doing their job?
Why do doctors choose to see reps?
Where do I find better sources of info?
How do reps influence doctors?
Where is the harm in promoting approved product information?
Won't patients miss out on sample medication?
Will signing the pledge harm the pharmaceutical industry?
Have any drug companies been found guilty of misrepresentation?
Listed below are the comments from doctors who have pedged to not see visiting pharmaceutical representatives (drug reps) at their practice for one year. Please note that doctors who sign the pledge can do so without making it public and without leaving a comment, so this is not a complete list of all signatories.
Click on the doctor's name to view their information.
I believe, with the wide availability of independent medication information, I do not require promotional material from visiting sales representatives from pharmaceutical companies ('Drug Reps'). The primary role of the Drug Rep is marketing: encouraging doctors to prescribe their company's medications. This conflicts with my role to provide the best evidence-based treatment and independent advice to my patients. I therefore take this 'No Advertising Please' pledge and choose not to see Drug Reps at my practice for one year.
Why have you supported the No Advertising Please campaign?
No Advertising Please encourages doctors to avoid using drug representatives as their 'educational' resource, by pledging to not see drug reps at their practice for one year. Research shows that doctors who see these drug reps are more likely to prescribe more medication, more expensively and less according to accepted guidelines.


text:
Simon Clark studied Drama, Art and Education at the University of Hertfordshire and was a Postgraduate Director at the London Academy of the Dramatic Arts (LAMDA), where he worked with various directors, including Ken Campbell and Peter James.
In 1989, he founded theatre works and the theatre works theatre Festival. The company and Festival specialised in original, regional productions, collaborating with playwrights, national and international companies, including Jonathan Harvey, Paines Plough and the Romanian National Youth Theatre. Simon's own plays include: "Portrait of the Artist as a Young Conservative" (winner of The Guardian Critic's Choice, Edinburgh '91), "Ulster Mourning" (commissioned by the Theatre Royal, Stratford East), the Lincoln Community Play: "Devils Let Loose" and "The Nearly Men" (produced with Wasps vs Humans, premiere April 2017)
In 1999, Simon established Burning Paper Productions. The company worked in partnership with various television production companies, including Ronin Entertainment and Littler Productions. Projects included: "Passion" a short film: Highly Commended at the TCM Film Festival; "The Nearly Men", a comedy series and "Molly's World" a children's cartoon series – both series remain unproduced to date.
In 2015, he co-founded The Clark Brothers, a film and television creative production company, with his brother, writer and director Oliver Clark. The company's current projects include "Harder" a brutally funny police drama series and "The Award-Winning Clark Bros Really Really Really Good Show" – web series to be broadcast from March 2017.
In 2016, at Dublin Web Fest, The Clark Brothers won the Best Original Writing Award.
Writing credits/Filmography:
Portrait of the Artist as Young Conservative (play: The Guardian's Critic's Choice, Edinburgh '91)
The Last of the Bohemians (play: co-production Bishop Grosseteste University)
Ulster Mourning (play: commissioned by the Theatre Royal, Stratford East)
Hurly Burly (play: commissioned by The Theatre Royal, Lincoln)
Macbed (film, produced at LAMDA in association with Ken Campbell)
Oh Minnie (film: Burning Paper Productions)
Passion (film: "Highly Commended" TCM Film Festival; Burning Paper Productions)
The Nearly Men (trailer, Burning Paper Productions)
Molly's World (animatic, Burning Paper Productions)
Incident 91 (film, produced in association with Lincolnshire Police)
Devils Let Loose (play: commissioned and produced by the Lincoln Community Play Association)
The Nearly Men (co-writer: Wasps vs Humans, play: premiere April '17)
Harder: web/TV series
The Award Winning Clark Bros Really Really Really Good Show. (web series: premiere Mar '17)
Oliver Clark studied English, American, Italian literature, as well as journalism and communication science, at the Universities of Goettingen and Florence. Combining his studies with the fields of film and television he wrote his Master's thesis on "Forms of cinematic and literary narration in the works of Robert Altman and Raymond Carver".
A freelance filmmaker and voice artist since 1996, he also worked as author, film reviewer, teacher, book seller, film projectionist. In 1995 he co-founded the Goettingen Low Budget Filmfestival, which in the following years featured early short films of renowned fellow filmmakers such as Fatih Akin, Sven Taddicken, Gordian Maugg. In 1998 he established SurfCut film&video, the company successfully produced various music videos, corporate videos and commercials over the following 17 years.
In 2011, Oliver co-founded Harderfilm, a company specialized in the production of feature films, with producer Daniel Bernhard. The feature length crime drama "Harder and the Girl" was produced in 2012/2013, the science fiction short "White collar Upgrade" is currently in post production.
In 2015, he co-founded The Clark Brothers, a film and television creative production company, with his brother, writer and director Simon Clark. The company's current projects include "Harder" a brutally funny police drama series and "The Award-Winning Clark Brothers' Really Really Really Good Show" – web series to be broadcast from March 2017.
Selected Filmography
1996 Thomas Stearns Eliot: Eyes That Last I Saw In Tears (experimental poetry video)
2000 Sugareen: Black and Blue (music video for Enola Music)
2002 Aldo Palazzeschi: Mar Grigio (experimental poetry video)
2004 Robert Frost: The Road Not Taken (experimental poetry video)
2007 Camera: Manfred Krueger (documentary portrait video)
2007 William Blake: To Summer (experimental poetry video)
2008 MK Bio – Tailor-made Immuno Assays (corporate video for MK Bio Shanghai)
2009 Projekt Pandora: Kleiner Wicht (music video for Sequential Media)
2011 Global City: International Gardens (documentary)
2013 Harder and the Girl (feature film with Harderfilm)
2013 Heartburn Billy And His Burning Harz: Schorsch Clooney (music video)
2015 City of Goettingen (corporate video for Goettingen Tourism and Nortia)
2016 Uwe Kolkmeyer und das Bohonga Spezial Ensemble: Spuren der Nacht (music video)
2016 Harder – The Series (TV series: in preproduction)
2017 The Award Winning Clark Brothers' Really Really Really Good Show (web series: premiere Mar '17)


text:
Wholesale Video Games – More Gaming For Less Money
by Admin February 7, 2022
The universe of computer games has expanded significantly in scope lately. The abilities of equipment and programming have developed staggeringly alongside the assortment of games that you can play. Contrast a more established game stage and an advanced one and you will handily see the distinction.
Any individual who has been a gamer for a very long time will have seen how much the video gaming world has extended. Further developed equipment and programming joined with the force of the web has made it conceivable to make virtual universes where gamers can reside in the pretense of a virtual person that they make and control. By making more games accessible at a lower value, discount computer games will extend this universe considerably more.
In the event that you are not that keen on purchasing the most recent games available, however are more keen on purchasing a few more established ones immediately, then, at that point, purchasing discount computer games will interest you. You might even have the option to bring in cash to pay for your gaming propensity by purchasing the games discount and afterward exchanging them online to create a gain.
เว็บแทงบอล Buying discount computer games is an incredible method for getting a great deal of gaming delight from a limited quantity of cash. Getting them in amount gets you a ton of games for a lower venture than you would get on the off chance that you got them each in turn at retail cost.
Those individuals that are playing those games in this style, when you don't have the most recent game it is okay on the grounds that when their wrapped up playing the game previously, the freshest games are not the most up to date any longer. This is exceptionally valid for sporting events that come out each year. The current year will be obsolete when your prepared to play the game assuming you take as much time as is needed attempting to complete the earlier year.
Web based playing has changed computer games, even the re-playability of the games. There are in every case new challengers and new difficulties out there with the web based gaming framework, which promotes how much time a player will keep playing a game. This proceeds with the contention for proceeding to play the web based games you bought at a less expensive value that will


I've been working on a tangential project related to NBusiness for a couple of weeks now and I just wanted to take a moment to get a few of my thoughts out. The project I have been working on I am tentatively calling "MetaSharp" for now. It's been fun and educational but hopefully it will have real usefullness when it is done. I wanted to have a fully working example before I publicly posted the code (since it's basically prototype quality right now) but if anyone is interested in seeing what I have so far feel free to ask and I'll hook you up somehow.
I'll try to start at the beginning to justify my rationale for creating this strange project. I've been working on NBusiness for quite a while now and while I've really had NBusiness "working" almost all along I have never quite been able to get it where I want it to be (complete). If I had to sum up the entire process of working on NBusiness into one sentence it would be "creating a DSL is hard". That's an understatement frankly. Let me see if I can lay out the various layers required for DSL creation.
The first three items are actually relatively easy and pretty fun. This is what we all know how to do, write code to parse strings and stick values into objects. No problem. It turns out the next three layers which really provide the fit, finish and ultimate usability of your DSL are not easy at all. Build integration isn't really that bad actually but tooling integration can be a real bear. In the case of a DSL you really want syntax hilighting and intellisense and nice IDE integration for file templates and things like that. Maybe a few additional context menus in your IDE and such. For me I have been trying to integrate into Visual Studio and I can officially say that I have sunk well over half my time into that aspect alone and it has been one of the hardest things I have ever tried to do. Visual Studio is also architected such that I had to completely redo my parser and compiler to be compatible with the needs of Visual Studio. Very painful.
But what is really hanging me up now is what I consider to be a large gap in the .NET DSL world and that is a suitable templating engine. By templating engine I mean something that can take metadata and translate it into code.
I mean we have a bunch out there but they're all (as far as I know) effectively giant string builders. They suffer from Tag Soup and and are bound strongly to a specific language implementation. For NBusiness I want to support side by side integration with any .NET language, C# or VB or Python or whatever. And re-creating all of these templates for every language is not an option. It's too much upfront work and it's too much long term maintenance. I absolutely need templates that are based on the CodeDom so I can be language agnostic… But if you've ever tried to use the CodeDom you know how hard it is to work with. Because of this users are very unlikely to actually make their own templates (which is almost always necessary) and when they do it is a very painful process. So I've been stuck in this cunundrum for quite a while, how can you make a template engine that is both based on the CodeDom but has the ease of use of a string builder?
Which when compiled will generate a class called OneTemplate that inherits from Template and returns a CodeTypeDeclaration object from it's Generate method. Extensions such as the BindingExtension show here can be custom objects to extend behaviors but in this case it binds the name of the class to the Name property (or Name sequence node of an MGraph tree) of the provided metadata.
Technically you could write your entire project in pure MetaSharp code but more likely you will write all of your static classes in your rich language of choice and simply use MetaSharp to define templates. Since this is all compiling down to CodeDom objects I have cooked up some MSBuild tasks that simply translate those objects into the code of the project the files exist in. You could share this same file in a VB or C# project and it would compile to the same thing in both assemblies.
Currently I am working on a prototype using the Song example from the MGrammar sample code that will allow you to write songs that generate song classes using templates like these. It's almost working… the CSharpCodeProvider is throwing a random NullReferenceException with no useful error messages. Which is one reason why a DSL like this is helpful, it should be able to abstract away the pain of working directly with the CodeDom.


text:
Q: C++ OpenSSL hash of the file is not the right one I want to calculate Sha1 of any given file in C++ using OpenSSL library.
I have read any article on the internet (including all from stackoverflow too) about doing this for almost 3 days.
Finally I get my program to work but the generated hash of any given file is not as it should be.
My code is someway similar to these found here and here but more easy to read and to use further in my program I write.
Also, I want to use C++ code not C code as they are written in the links above, second, they use:
SHA256_Init(&context);
 SHA256_Update(&context, (unsigned char*)input, length);
 SHA256_Final(md, &context);

which aren't available anymore in the new/current OpenSSL version (3.0 or so, I think).
So, I think this question will help many other readers that I observe meet the same problem(s) I do with the new OpenSSL version and can not use old code samples anymore.
This is my C++ code that is created to read huge files by chuncks without loading them into memory (hope this will help future readers of this post because it have many useful lines but it is not fully working as you will see):
bool hashFullFile(const std::string& FilePath, std::string &hashed, std::string &hash_type) {
    bool success = false;
    EVP_MD_CTX *context = EVP_MD_CTX_new();
    //read file by chuncks:
    const int BUFFER_SIZE = 1024;
    std::vector<char> buffer (BUFFER_SIZE + 1, 0);

    // check if the file to read from exists and if so read the file in chunks
    std::ifstream fin(FilePath, std::ifstream::binary | std::ifstream::in);

    if (hash_type == "SHA1") {
        if (context != NULL) {
            if (EVP_DigestInit_ex(context, EVP_sha1(), NULL)) {
                while (fin.good()){

                    fin.read(buffer.data(), BUFFER_SIZE);
                    std::streamsize s = ((fin) ? BUFFER_SIZE : fin.gcount());
                    buffer[s] = 0;
                    //convert vector of chars to string:
                    std::string str(buffer.data());
                    if (!EVP_DigestUpdate(context, str.c_str(), str.length())) {
                        fprintf(stderr, "Error while digesting file.\n");
                        return false;
                    }

                }
                unsigned char hash[EVP_MAX_MD_SIZE];
                unsigned int lengthOfHash = 0;
                if (EVP_DigestFinal_ex(context, hash, &lengthOfHash)) {
                    std::stringstream ss;
                    for (unsigned int i = 0; i < lengthOfHash; ++i) {
                        ss << std::hex << std::setw(2) << std::setfill('0') << (int) hash[i];
                    }

                    hashed = ss.str();
                    success = true;
                }else{
                    fprintf(stderr, "Error while finalizing digest.\n");
                    return false;
                }
            }else{
                fprintf(stderr, "Error while initializing digest context.\n");
                return false;
            }
            EVP_MD_CTX_free(context);
        }else{
            fprintf(stderr, "Error while creating digest context.\n");
            return false;
        }
    }
    fin.close();
    return success;
}

And I am using it like this into main function:
std::string myhash;
std::string myhash_type = "SHA1";
hashFullFile(R"(C:\Users\UserName\data.bin)", myhash, myhash_type);
cout<<myhash<<endl;

The problem is that for a given file it calculates hash:
e.g. 169ed28c9796a8065f96c98d205f21ddac11b14e  as the hash output but the same file has the hash:
 openssl dgst -sha1 data.bin
SHA1(data.bin)= 1927f720a858d0c3b53893695879ae2a7897eedb

generated by Openssl command line and also by any site from the internet.
I can't figure out what am I doing wrong since my code seems to be correct.
Please help.
Thank you very much in advance!

A: You're missing the finishing calculation on your EVP API attempt. The use of an intermediate string is unnecessary as well. Finally, the function should return the digest as a vector of bytes. let the caller do with that what they want.
Examples using both the EVP API and a BIO chain are shown below.
#include <iostream>
#include <fstream>
#include <algorithm>
#include <array>
#include <vector>
#include <memory>

#include <openssl/evp.h>
#include <openssl/sha.h>

namespace
{
    struct Delete
    {
        void operator()(BIO * p) const
        {
            BIO_free(p);
        }

        void operator()(EVP_MD_CTX *p) const
        {
            EVP_MD_CTX_free(p);
        }
    };

    using BIO_ptr = std::unique_ptr<BIO, Delete>;
    using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, Delete>;
}

std::vector<uint8_t> hashFileEVP(const std::string &fname, std::string const &mdname = "sha1")
{
    // will hold the resulting digest
    std::vector<uint8_t> md;

    // set this to however big you want the chunk size to be
    static constexpr size_t BUFFER_SIZE = 1024;
    std::array<char, BUFFER_SIZE> buff;

    // get the digest algorithm by name
    const EVP_MD *mthd = EVP_get_digestbyname(mdname.c_str());
    if (mthd)
    {
        std::ifstream inp(fname, std::ios::in | std::ios::binary);
        if (inp.is_open())
        {
            EVP_MD_CTX_ptr ctx{EVP_MD_CTX_new()};
            EVP_DigestInit_ex(ctx.get(), mthd, nullptr);

            while (inp.read(buff.data(), BUFFER_SIZE).gcount() > 0)
                EVP_DigestUpdate(ctx.get(), buff.data(), inp.gcount());

            // size output vector
            unsigned int mdlen = EVP_MD_size(mthd);
            md.resize(mdlen);

            // general final digest
            EVP_DigestFinal_ex(ctx.get(), md.data(), &mdlen);
        }
    }
    return md;
}

std::vector<uint8_t> hashFileBIO(std::string const &fname, std::string const &mdname = "sha1")
{
    // the fixed-size read buffer
    static constexpr size_t BUFFER_SIZE = 1024;

    // will hold the resulting digest
    std::vector<uint8_t> md;

    // select this however you want.
    const EVP_MD *mthd = EVP_get_digestbyname(mdname.c_str());
    if (mthd)
    {
        // open the file and a message digest BIO
        BIO_ptr bio_f(BIO_new_file(fname.c_str(), "rb"));
        BIO_ptr bio_md(BIO_new(BIO_f_md()));
        BIO_set_md(bio_md.get(), mthd);

        // chain the bios together. note this bio is NOT
        //  held together with a smart pointer; all the
        //  bios in the chain are.
        BIO *bio = BIO_push(bio_md.get(), bio_f.get());

        // read through file one buffer at a time.
        std::array<char, BUFFER_SIZE> buff;
        while (BIO_read(bio, buff.data(), buff.size()) > 0)
            ; // intentionally empty

        // size output buffer
        unsigned int mdlen = EVP_MD_size(mthd);
        md.resize(mdlen);

        // read final digest from md bio.
        BIO_gets(bio_md.get(), (char *)md.data(), mdlen);
    }
    return md;
}

// convert a vector of byte to std::string
std::string bin2hex(std::vector<uint8_t> const& bin)
{
    std::string res;
    size_t len = 0;
    if (OPENSSL_buf2hexstr_ex(nullptr, 0, &len, bin.data(), bin.size(), 0) != 0)
    {
        res.resize(len);
        OPENSSL_buf2hexstr_ex(&res[0], len, &len, bin.data(), bin.size(), 0);
    }
    return res;
}

int main()
{
    OpenSSL_add_all_digests();

    // i have this on my rig. use whatever you want
    //  or get the name from argv or some such.
    static const char fname[] = "dictionary.txt";

    auto md1 = hashFileEVP(fname);
    auto md1str = bin2hex(md1);
    std::cout << "hashed with EVP API\n";
    std::cout << md1str << '\n';

    auto md2 = hashFileBIO(fname);
    auto md2str = bin2hex(md1);
    std::cout << "hashed with BIO chain\n";
    std::cout << md2str << '\n';
}

Output
hashed with EVP API
0A97D663ADA2E039FD904846ABC5361291BD2D8E
hashed with BIO chain
0A97D663ADA2E039FD904846ABC5361291BD2D8E

Output from openssl command line
craig@rogue1 % openssl dgst -sha1 dictionary.txt
SHA1(dictionary.txt)= 0a97d663ada2e039fd904846abc5361291bd2d8e

Note the digests are the same in all three cases.

redpajama_set_name: RedPajamaStackExchange