Commit 2973655f authored by Cristian Maglie's avatar Cristian Maglie

Migrated board-discovery to use go.bug.st/serial.v1 lib

parent f44aaa57
......@@ -22,7 +22,7 @@
branch = "master"
name = "github.com/arduino/board-discovery"
packages = ["."]
revision = "e15a7a617732d07533e9cef7ac5318bc637e9575"
revision = "1ba29327fb0cd77c44823490c43433475f5379c6"
[[projects]]
branch = "master"
......@@ -153,12 +153,6 @@
revision = "7f532489e7739b3d49df5c602bf63549881fe753"
version = "v5.0.1"
[[projects]]
branch = "master"
name = "github.com/facchinm/go-serial-native"
packages = ["."]
revision = "af997a46fb5a5ad87469f1dd6a1bb86918f259ba"
[[projects]]
name = "github.com/fatih/color"
packages = ["."]
......@@ -188,6 +182,15 @@
revision = "6529cf7c58879c08d927016dde4477f18a0634cb"
version = "v1.36.0"
[[projects]]
name = "github.com/go-ole/go-ole"
packages = [
".",
"oleutil"
]
revision = "a41e3c4b706f6ae8dfbff342b06e40fa4d2d0506"
version = "v1.2.1"
[[projects]]
name = "github.com/goadesign/goa"
packages = [
......@@ -355,6 +358,7 @@
name = "go.bug.st/serial.v1"
packages = [
".",
"enumerator",
"unixutils"
]
revision = "eae1344f9f90101f887b08d13391c34399f97873"
......
......@@ -63,16 +63,15 @@ import (
"time"
"github.com/codeclysm/cc"
serial "github.com/facchinm/go-serial-native"
)
// SerialDevice is something connected to the Serial Ports
type SerialDevice struct {
Port string `json:"port"`
SerialNumber string `json:"serial_number"`
ProductID string `json:"pid"`
VendorID string `json:"vid"`
Serial *serial.Info `json:"-"`
Port string `json:"port"`
SerialNumber string `json:"serial_number"`
ProductID string `json:"pid"`
VendorID string `json:"vid"`
//Serial *serial.Info `json:"-"`
}
func (d SerialDevice) String() string {
......
......@@ -30,11 +30,10 @@
package discovery
import (
"fmt"
"time"
"github.com/facchinm/go-serial-native"
"github.com/juju/errors"
"go.bug.st/serial.v1/enumerator"
)
// Merge updates the device with the new one, returning false if the operation
......@@ -58,21 +57,20 @@ func (d *SerialDevice) merge(dev SerialDevice) bool {
d.VendorID = dev.VendorID
}
if d.Serial != dev.Serial {
d.Serial = dev.Serial
}
// if d.Serial != dev.Serial {
// d.Serial = dev.Serial
// }
return changed
}
func (m *Monitor) serialDiscover() error {
ports, err := serial.ListPorts()
ports, err := enumerator.GetDetailedPortsList()
if err != nil {
return errors.Annotatef(err, "while listing the serial ports")
}
for _, port := range ports {
m.addSerial(port)
}
m.pruneSerial(ports)
......@@ -80,18 +78,17 @@ func (m *Monitor) serialDiscover() error {
return nil
}
func (m *Monitor) addSerial(port *serial.Info) {
vid, pid, _ := port.USBVIDPID()
if vid == 0 || pid == 0 {
func (m *Monitor) addSerial(port *enumerator.PortDetails) {
if !port.IsUSB {
return
}
device := SerialDevice{
Port: port.Name(),
SerialNumber: port.USBSerialNumber(),
ProductID: fmt.Sprintf("0x%04X", pid),
VendorID: fmt.Sprintf("0x%04X", vid),
Serial: port,
Port: port.Name,
SerialNumber: port.SerialNumber,
ProductID: "0x" + port.PID,
VendorID: "0x" + port.VID,
//Serial: port,
}
for port, dev := range m.serial {
if port == device.Port {
......@@ -107,12 +104,12 @@ func (m *Monitor) addSerial(port *serial.Info) {
//m.Events <- Event{Name: "add", SerialDevice: &device}
}
func (m *Monitor) pruneSerial(ports []*serial.Info) {
func (m *Monitor) pruneSerial(ports []*enumerator.PortDetails) {
toPrune := []string{}
for port := range m.serial {
found := false
for _, p := range ports {
if port == p.Name() {
if port == p.Name {
found = true
}
}
......
# Go Serial
[![GoDoc](https://godoc.org/github.com/mikepb/go-serial?status.svg)](https://godoc.org/github.com/mikepb/go-serial)
Package serial provides a binding to libserialport for serial port
functionality. Serial ports are commonly used with embedded systems,
such as the Arduino platform.
## Usage
```go
package main
import (
"github.com/mikepb/go-serial"
"log"
)
func main() {
options := serial.RawOptions
options.BitRate = 115200
p, err := options.Open("/dev/tty")
if err != nil {
log.Panic(err)
}
defer p.Close()
buf := make([]byte, 1)
if c, err := p.Read(buf); err != nil {
log.Panic(err)
} else {
log.Println(buf)
}
}
```
## Documentation
https://godoc.org/github.com/mikepb/go-serial
## License
Copyright 2014 Michael Phan-Ba
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Files from the libserialport library are licensed under the GNU Lesser General
Public License. These files include in the header the notice that follows.
This file is part of the libserialport project.
Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
Copyright (C) 2010-2012 Uwe Hermann <uwe@hermann-uwe.de>
Copyright (C) 2013-2014 Martin Ling <martin-libserialport@earth.li>
Copyright (C) 2013 Matthias Heidbrink <m-sigrok@heidbrink.biz>
Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
* This file is part of the libserialport project.
*
* Copyright (C) 2014 Martin Ling <martin-libserialport@earth.li>
* Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef __linux__
#define _BSD_SOURCE // for timeradd, timersub, timercmp
#endif
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#ifdef _WIN32
#include <windows.h>
#include <tchar.h>
#include <setupapi.h>
#include <cfgmgr32.h>
#undef DEFINE_GUID
#define DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \
static const GUID name = { l,w1,w2,{ b1,b2,b3,b4,b5,b6,b7,b8 } }
#include <usbioctl.h>
#include <usbiodef.h>
#else
#include <limits.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <limits.h>
#include <poll.h>
#endif
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#include <IOKit/serial/ioss.h>
#include <sys/syslimits.h>
#endif
#ifdef __linux__
#include <dirent.h>
#ifndef __ANDROID__
#include "linux/serial.h"
#endif
#include "linux_termios.h"
/* TCGETX/TCSETX is not available everywhere. */
#if defined(TCGETX) && defined(TCSETX) && defined(HAVE_TERMIOX)
#define USE_TERMIOX
#endif
#endif
/* TIOCINQ/TIOCOUTQ is not available everywhere. */
#if !defined(TIOCINQ) && defined(FIONREAD)
#define TIOCINQ FIONREAD
#endif
#if !defined(TIOCOUTQ) && defined(FIONWRITE)
#define TIOCOUTQ FIONWRITE
#endif
/* Non-standard baudrates are not available everywhere. */
#if (defined(HAVE_TERMIOS_SPEED) || defined(HAVE_TERMIOS2_SPEED)) && defined(HAVE_BOTHER)
#define USE_TERMIOS_SPEED
#endif
struct sp_port {
char *name;
char *description;
enum sp_transport transport;
int usb_bus;
int usb_address;
int usb_vid;
int usb_pid;
char *usb_manufacturer;
char *usb_product;
char *usb_serial;
char *bluetooth_address;
#ifdef _WIN32
char *usb_path;
HANDLE hdl;
COMMTIMEOUTS timeouts;
OVERLAPPED write_ovl;
OVERLAPPED read_ovl;
OVERLAPPED wait_ovl;
DWORD events;
BYTE pending_byte;
BOOL writing;
BOOL composite;
#else
int fd;
#endif
};
struct sp_port_config {
int baudrate;
int bits;
enum sp_parity parity;
int stopbits;
enum sp_rts rts;
enum sp_cts cts;
enum sp_dtr dtr;
enum sp_dsr dsr;
enum sp_xonxoff xon_xoff;
};
struct port_data {
#ifdef _WIN32
DCB dcb;
#else
struct termios term;
int controlbits;
int termiox_supported;
int rts_flow;
int cts_flow;
int dtr_flow;
int dsr_flow;
#endif
};
#ifdef _WIN32
typedef HANDLE event_handle;
#else
typedef int event_handle;
#endif
/* Standard baud rates. */
#ifdef _WIN32
#define BAUD_TYPE DWORD
#define BAUD(n) {CBR_##n, n}
#else
#define BAUD_TYPE speed_t
#define BAUD(n) {B##n, n}
#endif
struct std_baudrate {
BAUD_TYPE index;
int value;
};
extern const struct std_baudrate std_baudrates[];
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#define NUM_STD_BAUDRATES ARRAY_SIZE(std_baudrates)
extern void (*sp_debug_handler)(const char *format, ...);
/* Debug output macros. */
#define DEBUG_FMT(fmt, ...) do { \
if (sp_debug_handler) \
sp_debug_handler(fmt ".\n", __VA_ARGS__); \
} while (0)
#define DEBUG(msg) DEBUG_FMT(msg, NULL)
#define DEBUG_ERROR(err, msg) DEBUG_FMT("%s returning " #err ": " msg, __func__)
#define DEBUG_FAIL(msg) do { \
char *errmsg = sp_last_error_message(); \
DEBUG_FMT("%s returning SP_ERR_FAIL: " msg ": %s", __func__, errmsg); \
sp_free_error_message(errmsg); \
} while (0);
#define RETURN() do { \
DEBUG_FMT("%s returning", __func__); \
return; \
} while(0)
#define RETURN_CODE(x) do { \
DEBUG_FMT("%s returning " #x, __func__); \
return x; \
} while (0)
#define RETURN_CODEVAL(x) do { \
switch (x) { \
case SP_OK: RETURN_CODE(SP_OK); \
case SP_ERR_ARG: RETURN_CODE(SP_ERR_ARG); \
case SP_ERR_FAIL: RETURN_CODE(SP_ERR_FAIL); \
case SP_ERR_MEM: RETURN_CODE(SP_ERR_MEM); \
case SP_ERR_SUPP: RETURN_CODE(SP_ERR_SUPP); \
} \
} while (0)
#define RETURN_OK() RETURN_CODE(SP_OK);
#define RETURN_ERROR(err, msg) do { \
DEBUG_ERROR(err, msg); \
return err; \
} while (0)
#define RETURN_FAIL(msg) do { \
DEBUG_FAIL(msg); \
return SP_ERR_FAIL; \
} while (0)
#define RETURN_INT(x) do { \
int _x = x; \
DEBUG_FMT("%s returning %d", __func__, _x); \
return _x; \
} while (0)
#define RETURN_STRING(x) do { \
char *_x = x; \
DEBUG_FMT("%s returning %s", __func__, _x); \
return _x; \
} while (0)
#define RETURN_POINTER(x) do { \
void *_x = x; \
DEBUG_FMT("%s returning %p", __func__, _x); \
return _x; \
} while (0)
#define SET_ERROR(val, err, msg) do { DEBUG_ERROR(err, msg); val = err; } while (0)
#define SET_FAIL(val, msg) do { DEBUG_FAIL(msg); val = SP_ERR_FAIL; } while (0)
#define TRACE(fmt, ...) DEBUG_FMT("%s(" fmt ") called", __func__, __VA_ARGS__)
#define TRACE_VOID() DEBUG_FMT("%s() called", __func__)
#define TRY(x) do { int ret = x; if (ret != SP_OK) RETURN_CODEVAL(ret); } while (0)
SP_PRIV struct sp_port **list_append(struct sp_port **list, const char *portname);
/* OS-specific Helper functions. */
SP_PRIV enum sp_return get_port_details(struct sp_port *port);
SP_PRIV enum sp_return list_ports(struct sp_port ***list);
/*
* This file is part of the libserialport project.
*
* Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef __linux__
#include "libserialport.h"
#include "libserialport_internal.h"
SP_PRIV enum sp_return get_port_details(struct sp_port *port)
{
/* Description limited to 127 char,
anything longer would not be user friendly anyway */
char description[128];
int bus, address;
unsigned int vid, pid;
char manufacturer[128], product[128], serial[128];
char baddr[32];
const char dir_name[] = "/sys/class/tty/%s/device/%s%s";
char sub_dir[32] = "", file_name[PATH_MAX];
char *ptr, *dev = port->name + 5;
FILE *file;
int i, count;
if (strncmp(port->name, "/dev/", 5))
RETURN_ERROR(SP_ERR_ARG, "Device name not recognized.");
snprintf(file_name, sizeof(file_name), "/sys/class/tty/%s", dev);
count = readlink(file_name, file_name, sizeof(file_name));
if (count <= 0 || count >= (int) sizeof(file_name)-1)
RETURN_ERROR(SP_ERR_ARG, "Device not found.");
file_name[count] = 0;
if (strstr(file_name, "bluetooth"))
port->transport = SP_TRANSPORT_BLUETOOTH;
else if (strstr(file_name, "usb"))
port->transport = SP_TRANSPORT_USB;
if (port->transport == SP_TRANSPORT_USB) {
for (i=0; i<5; i++) {
strcat(sub_dir, "../");
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "busnum");
if (!(file = fopen(file_name, "r")))
continue;
count = fscanf(file, "%d", &bus);
fclose(file);
if (count != 1)
continue;
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "devnum");
if (!(file = fopen(file_name, "r")))
continue;
count = fscanf(file, "%d", &address);
fclose(file);
if (count != 1)
continue;
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "idVendor");
if (!(file = fopen(file_name, "r")))
continue;
count = fscanf(file, "%4x", &vid);
fclose(file);
if (count != 1)
continue;
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "idProduct");
if (!(file = fopen(file_name, "r")))
continue;
count = fscanf(file, "%4x", &pid);
fclose(file);
if (count != 1)
continue;
port->usb_bus = bus;
port->usb_address = address;
port->usb_vid = vid;
port->usb_pid = pid;
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "product");
if ((file = fopen(file_name, "r"))) {
if ((ptr = fgets(description, sizeof(description), file))) {
ptr = description + strlen(description) - 1;
if (ptr >= description && *ptr == '\n')
*ptr = 0;
port->description = strdup(description);
}
fclose(file);
}
if (!file || !ptr)
port->description = strdup(dev);
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "manufacturer");
if ((file = fopen(file_name, "r"))) {
if ((ptr = fgets(manufacturer, sizeof(manufacturer), file))) {
ptr = manufacturer + strlen(manufacturer) - 1;
if (ptr >= manufacturer && *ptr == '\n')
*ptr = 0;
port->usb_manufacturer = strdup(manufacturer);
}
fclose(file);
}
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "product");
if ((file = fopen(file_name, "r"))) {
if ((ptr = fgets(product, sizeof(product), file))) {
ptr = product + strlen(product) - 1;
if (ptr >= product && *ptr == '\n')
*ptr = 0;
port->usb_product = strdup(product);
}
fclose(file);
}
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "serial");
if ((file = fopen(file_name, "r"))) {
if ((ptr = fgets(serial, sizeof(serial), file))) {
ptr = serial + strlen(serial) - 1;
if (ptr >= serial && *ptr == '\n')
*ptr = 0;
port->usb_serial = strdup(serial);
}
fclose(file);
}
break;
}
} else {
port->description = strdup(dev);
if (port->transport == SP_TRANSPORT_BLUETOOTH) {
snprintf(file_name, sizeof(file_name), dir_name, dev, "", "address");
if ((file = fopen(file_name, "r"))) {
if ((ptr = fgets(baddr, sizeof(baddr), file))) {
ptr = baddr + strlen(baddr) - 1;
if (ptr >= baddr && *ptr == '\n')
*ptr = 0;
port->bluetooth_address = strdup(baddr);
}
fclose(file);
}
}
}
RETURN_OK();
}
SP_PRIV enum sp_return list_ports(struct sp_port ***list)
{
char name[PATH_MAX], target[PATH_MAX];
struct dirent entry, *result;
#ifdef HAVE_SERIAL_STRUCT
struct serial_struct serial_info;
int ioctl_result;
#endif
char buf[sizeof(entry.d_name) + 16];
int len, fd;
DIR *dir;
int ret = SP_OK;
DEBUG("Enumerating tty devices");
if (!(dir = opendir("/sys/class/tty")))
RETURN_FAIL("could not open /sys/class/tty");
DEBUG("Iterating over results");
while (!readdir_r(dir, &entry, &result) && result) {
snprintf(buf, sizeof(buf), "/sys/class/tty/%s", entry.d_name);
len = readlink(buf, target, sizeof(target));
if (len <= 0 || len >= (int) sizeof(target)-1)
continue;
target[len] = 0;
if (strstr(target, "virtual"))
continue;
snprintf(name, sizeof(name), "/dev/%s", entry.d_name);
DEBUG_FMT("Found device %s", name);
if (strstr(target, "serial8250")) {
/* The serial8250 driver has a hardcoded number of ports.
* The only way to tell which actually exist on a given system
* is to try to open them and make an ioctl call. */
DEBUG("serial8250 device, attempting to open");
if ((fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) {
DEBUG("open failed, skipping");
continue;
}
#ifdef HAVE_SERIAL_STRUCT
ioctl_result = ioctl(fd, TIOCGSERIAL, &serial_info);
#endif
close(fd);
#ifdef HAVE_SERIAL_STRUCT
if (ioctl_result != 0) {
DEBUG("ioctl failed, skipping");
continue;
}
if (serial_info.type == PORT_UNKNOWN) {
DEBUG("port type is unknown, skipping");
continue;
}
#endif
}
DEBUG_FMT("Found port %s", name);
*list = list_append(*list, name);
if (!list) {
SET_ERROR(ret, SP_ERR_MEM, "list append failed");
break;
}
}
closedir(dir);
return ret;
}
#endif
/*
* This file is part of the libserialport project.
*
* Copyright (C) 2013 Martin Ling <martin-libserialport@earth.li>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* At the time of writing, glibc does not support the Linux kernel interfaces
* for setting non-standard baud rates and flow control. We therefore have to
* prepare the correct ioctls ourselves, for which we need the declarations in
* linux/termios.h.
*
* We can't include linux/termios.h in serialport.c however, because its
* contents conflict with the termios.h provided by glibc. So this file exists
* to isolate the bits of code which use the kernel termios declarations.
*
* The details vary by architecture. Some architectures have c_ispeed/c_ospeed
* in struct termios, accessed with TCSETS/TCGETS. Others have these fields in
* struct termios2, accessed with TCSETS2/TCGETS2. Some architectures have the
* TCSETX/TCGETX ioctls used with struct termiox, others do not.
*/
#ifdef __linux__
#include <stdlib.h>
#include <linux/termios.h>
#include "linux_termios.h"
SP_PRIV unsigned long get_termios_get_ioctl(void)
{
#ifdef HAVE_TERMIOS2
return TCGETS2;
#else
return TCGETS;
#endif
}
SP_PRIV unsigned long get_termios_set_ioctl(void)
{
#ifdef HAVE_TERMIOS2
return TCSETS2;
#else
return TCSETS;
#endif
}
SP_PRIV size_t get_termios_size(void)
{
#ifdef HAVE_TERMIOS2
return sizeof(struct termios2);
#else
return sizeof(struct termios);
#endif
}
#if (defined(HAVE_TERMIOS_SPEED) || defined(HAVE_TERMIOS2_SPEED)) && defined(HAVE_BOTHER)
SP_PRIV int get_termios_speed(void *data)
{
#ifdef HAVE_TERMIOS2
struct termios2 *term = (struct termios2 *) data;
#else
struct termios *term = (struct termios *) data;
#endif
if (term->c_ispeed != term->c_ospeed)
return -1;
else
return term->c_ispeed;
}
SP_PRIV void set_termios_speed(void *data, int speed)
{
#ifdef HAVE_TERMIOS2
struct termios2 *term = (struct termios2 *) data;
#else
struct termios *term = (struct termios *) data;
#endif
term->c_cflag &= ~CBAUD;
term->c_cflag |= BOTHER;
term->c_ispeed = term->c_ospeed = speed;
}
#endif
#ifdef HAVE_TERMIOX
SP_PRIV size_t get_termiox_size(void)
{
return sizeof(struct termiox);
}
SP_PRIV int get_termiox_flow(void *data, int *rts, int *cts, int *dtr, int *dsr)
{
struct termiox *termx = (struct termiox *) data;
int flags = 0;
*rts = (termx->x_cflag & RTSXOFF);
*cts = (termx->x_cflag & CTSXON);
*dtr = (termx->x_cflag & DTRXOFF);
*dsr = (termx->x_cflag & DSRXON);
return flags;
}
SP_PRIV void set_termiox_flow(void *data, int rts, int cts, int dtr, int dsr)
{
struct termiox *termx = (struct termiox *) data;
termx->x_cflag &= ~(RTSXOFF | CTSXON | DTRXOFF | DSRXON);
if (rts)
termx->x_cflag |= RTSXOFF;
if (cts)
termx->x_cflag |= CTSXON;
if (dtr)
termx->x_cflag |= DTRXOFF;
if (dsr)
termx->x_cflag |= DSRXON;
}
#endif
#endif
/*
* This file is part of the libserialport project.
*
* Copyright (C) 2013 Martin Ling <martin-libserialport@earth.li>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef __linux__
#ifndef LIBSERIALPORT_LINUX_TERMIOS_H
#define LIBSERIALPORT_LINUX_TERMIOS_H
#include <stdlib.h>
SP_PRIV unsigned long get_termios_get_ioctl(void);
SP_PRIV unsigned long get_termios_set_ioctl(void);
SP_PRIV size_t get_termios_size(void);
SP_PRIV int get_termios_speed(void *data);
SP_PRIV void set_termios_speed(void *data, int speed);
SP_PRIV size_t get_termiox_size(void);
SP_PRIV int get_termiox_flow(void *data, int *rts, int *cts, int *dtr, int *dsr);
SP_PRIV void set_termiox_flow(void *data, int rts, int cts, int dtr, int dsr);
#endif
#endif
/*
* This file is part of the libserialport project.
*
* Copyright (C) 2013-2014 Martin Ling <martin-libserialport@earth.li>
* Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef __APPLE__
#include "libserialport.h"
#include "libserialport_internal.h"
SP_PRIV enum sp_return get_port_details(struct sp_port *port)
{
/* Description limited to 127 char,
anything longer would not be user friendly anyway */
char description[128];
int bus, address, vid, pid = -1;
char manufacturer[128], product[128], serial[128];
CFMutableDictionaryRef classes;
io_iterator_t iter;
io_object_t ioport, ioparent;
CFTypeRef cf_property, cf_bus, cf_address, cf_vendor, cf_product;
Boolean result;
char path[PATH_MAX], class[16];
DEBUG("Getting serial port list");
if (!(classes = IOServiceMatching(kIOSerialBSDServiceValue)))
RETURN_FAIL("IOServiceMatching() failed");
if (IOServiceGetMatchingServices(kIOMasterPortDefault, classes,
&iter) != KERN_SUCCESS)
RETURN_FAIL("IOServiceGetMatchingServices() failed");
DEBUG("Iterating over results");
while ((ioport = IOIteratorNext(iter))) {
if (!(cf_property = IORegistryEntryCreateCFProperty(ioport,
CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0))) {
IOObjectRelease(ioport);
continue;
}
result = CFStringGetCString(cf_property, path, sizeof(path),
kCFStringEncodingASCII);
CFRelease(cf_property);
if (!result || strcmp(path, port->name)) {
IOObjectRelease(ioport);
continue;
}
DEBUG_FMT("Found port %s", path);
IORegistryEntryGetParentEntry(ioport, kIOServicePlane, &ioparent);
if ((cf_property=IORegistryEntrySearchCFProperty(ioparent,kIOServicePlane,
CFSTR("IOProviderClass"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents))) {
if (CFStringGetCString(cf_property, class, sizeof(class),
kCFStringEncodingASCII) &&
strstr(class, "USB")) {
DEBUG("Found USB class device");
port->transport = SP_TRANSPORT_USB;
}
CFRelease(cf_property);
}
if ((cf_property=IORegistryEntrySearchCFProperty(ioparent,kIOServicePlane,
CFSTR("IOClass"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents))) {
if (CFStringGetCString(cf_property, class, sizeof(class),
kCFStringEncodingASCII) &&
strstr(class, "USB")) {
DEBUG("Found USB class device");
port->transport = SP_TRANSPORT_USB;
}
CFRelease(cf_property);
}
IOObjectRelease(ioparent);
if ((cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane,
CFSTR("USB Interface Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
(cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane,
CFSTR("USB Product Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
(cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane,
CFSTR("Product Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
(cf_property = IORegistryEntryCreateCFProperty(ioport,
CFSTR(kIOTTYDeviceKey), kCFAllocatorDefault, 0))) {
if (CFStringGetCString(cf_property, description, sizeof(description),
kCFStringEncodingASCII)) {
DEBUG_FMT("Found description %s", description);
port->description = strdup(description);
}
CFRelease(cf_property);
} else {
DEBUG("No description for this device");
}
cf_bus = IORegistryEntrySearchCFProperty(ioport, kIOServicePlane,
CFSTR("USBBusNumber"),
kCFAllocatorDefault,
kIORegistryIterateRecursively
| kIORegistryIterateParents);
cf_address = IORegistryEntrySearchCFProperty(ioport, kIOServicePlane,
CFSTR("USB Address"),
kCFAllocatorDefault,
kIORegistryIterateRecursively
| kIORegistryIterateParents);
if (cf_bus && cf_address &&
CFNumberGetValue(cf_bus , kCFNumberIntType, &bus) &&
CFNumberGetValue(cf_address, kCFNumberIntType, &address)) {
DEBUG_FMT("Found matching USB bus:address %03d:%03d", bus, address);
port->usb_bus = bus;
port->usb_address = address;
}
if (cf_bus ) CFRelease(cf_bus);
if (cf_address) CFRelease(cf_address);
cf_vendor = IORegistryEntrySearchCFProperty(ioport, kIOServicePlane,
CFSTR("idVendor"),
kCFAllocatorDefault,
kIORegistryIterateRecursively
| kIORegistryIterateParents);
cf_product = IORegistryEntrySearchCFProperty(ioport, kIOServicePlane,
CFSTR("idProduct"),
kCFAllocatorDefault,
kIORegistryIterateRecursively
| kIORegistryIterateParents);
if (cf_vendor && cf_product &&
CFNumberGetValue(cf_vendor , kCFNumberIntType, &vid) &&
CFNumberGetValue(cf_product, kCFNumberIntType, &pid)) {
DEBUG_FMT("Found matching USB vid:pid %04X:%04X", vid, pid);
port->usb_vid = vid;
port->usb_pid = pid;
}
if (cf_vendor ) CFRelease(cf_vendor);
if (cf_product) CFRelease(cf_product);
if ((cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane,
CFSTR("USB Vendor Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents))) {
if (CFStringGetCString(cf_property, manufacturer, sizeof(manufacturer),
kCFStringEncodingASCII)) {
DEBUG_FMT("Found manufacturer %s", manufacturer);
port->usb_manufacturer = strdup(manufacturer);
}
CFRelease(cf_property);
}
if ((cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane,
CFSTR("USB Product Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents))) {
if (CFStringGetCString(cf_property, product, sizeof(product),
kCFStringEncodingASCII)) {
DEBUG_FMT("Found product name %s", product);
port->usb_product = strdup(product);
}
CFRelease(cf_property);
}
if ((cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane,
CFSTR("USB Serial Number"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents))) {
if (CFStringGetCString(cf_property, serial, sizeof(serial),
kCFStringEncodingASCII)) {
DEBUG_FMT("Found serial number %s", serial);
port->usb_serial = strdup(serial);
}
CFRelease(cf_property);
}
IOObjectRelease(ioport);
break;
}
IOObjectRelease(iter);
RETURN_OK();
}
SP_PRIV enum sp_return list_ports(struct sp_port ***list)
{
CFMutableDictionaryRef classes;
io_iterator_t iter;
char path[PATH_MAX];
io_object_t port;
CFTypeRef cf_path;
Boolean result;
int ret = SP_OK;
DEBUG("Creating matching dictionary");
if (!(classes = IOServiceMatching(kIOSerialBSDServiceValue))) {
SET_FAIL(ret, "IOServiceMatching() failed");
goto out_done;
}
DEBUG("Getting matching services");
if (IOServiceGetMatchingServices(kIOMasterPortDefault, classes,
&iter) != KERN_SUCCESS) {
SET_FAIL(ret, "IOServiceGetMatchingServices() failed");
goto out_done;
}
DEBUG("Iterating over results");
while ((port = IOIteratorNext(iter))) {
cf_path = IORegistryEntryCreateCFProperty(port,
CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
if (cf_path) {
result = CFStringGetCString(cf_path, path, sizeof(path),
kCFStringEncodingASCII);
CFRelease(cf_path);
if (result) {
DEBUG_FMT("Found port %s", path);
if (!(*list = list_append(*list, path))) {
SET_ERROR(ret, SP_ERR_MEM, "list append failed");
IOObjectRelease(port);
goto out;
}
}
}
IOObjectRelease(port);
}
out:
IOObjectRelease(iter);
out_done:
return ret;
}
#endif
This diff is collapsed.
This diff is collapsed.
language: go
sudo: false
go:
- 1.1
- 1.2
- 1.3
- 1.4
- tip
# Version 1.x.x
* **Add more test cases and reference new test COM server project.** (Placeholder for future additions)
# Version 1.2.0-alphaX
**Minimum supported version is now Go 1.4. Go 1.1 support is deprecated, but should still build.**
* Added CI configuration for Travis-CI and AppVeyor.
* Added test InterfaceID and ClassID for the COM Test Server project.
* Added more inline documentation (#83).
* Added IEnumVARIANT implementation (#88).
* Added IEnumVARIANT test cases (#99, #100, #101).
* Added support for retrieving `time.Time` from VARIANT (#92).
* Added test case for IUnknown (#64).
* Added test case for IDispatch (#64).
* Added test cases for scalar variants (#64, #76).
# Version 1.1.1
* Fixes for Linux build.
* Fixes for Windows build.
# Version 1.1.0
The change to provide building on all platforms is a new feature. The increase in minor version reflects that and allows those who wish to stay on 1.0.x to continue to do so. Support for 1.0.x will be limited to bug fixes.
* Move GUID out of variables.go into its own file to make new documentation available.
* Move OleError out of ole.go into its own file to make new documentation available.
* Add documentation to utility functions.
* Add documentation to variant receiver functions.
* Add documentation to ole structures.
* Make variant available to other systems outside of Windows.
* Make OLE structures available to other systems outside of Windows.
## New Features
* Library should now be built on all platforms supported by Go. Library will NOOP on any platform that is not Windows.
* More functions are now documented and available on godoc.org.
# Version 1.0.1
1. Fix package references from repository location change.
# Version 1.0.0
This version is stable enough for use. The COM API is still incomplete, but provides enough functionality for accessing COM servers using IDispatch interface.
There is no changelog for this version. Check commits for history.
The MIT License (MIT)
Copyright © 2013-2017 Yasuhiro Matsumoto, <mattn.jp@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
#Go OLE
[![Build status](https://ci.appveyor.com/api/projects/status/qr0u2sf7q43us9fj?svg=true)](https://ci.appveyor.com/project/jacobsantos/go-ole-jgs28)
[![Build Status](https://travis-ci.org/go-ole/go-ole.svg?branch=master)](https://travis-ci.org/go-ole/go-ole)
[![GoDoc](https://godoc.org/github.com/go-ole/go-ole?status.svg)](https://godoc.org/github.com/go-ole/go-ole)
Go bindings for Windows COM using shared libraries instead of cgo.
By Yasuhiro Matsumoto.
## Install
To experiment with go-ole, you can just compile and run the example program:
```
go get github.com/go-ole/go-ole
cd /path/to/go-ole/
go test
cd /path/to/go-ole/example/excel
go run excel.go
```
## Continuous Integration
Continuous integration configuration has been added for both Travis-CI and AppVeyor. You will have to add these to your own account for your fork in order for it to run.
**Travis-CI**
Travis-CI was added to check builds on Linux to ensure that `go get` works when cross building. Currently, Travis-CI is not used to test cross-building, but this may be changed in the future. It is also not currently possible to test the library on Linux, since COM API is specific to Windows and it is not currently possible to run a COM server on Linux or even connect to a remote COM server.
**AppVeyor**
AppVeyor is used to build on Windows using the (in-development) test COM server. It is currently only used to test the build and ensure that the code works on Windows. It will be used to register a COM server and then run the test cases based on the test COM server.
The tests currently do run and do pass and this should be maintained with commits.
##Versioning
Go OLE uses [semantic versioning](http://semver.org) for version numbers, which is similar to the version contract of the Go language. Which means that the major version will always maintain backwards compatibility with minor versions. Minor versions will only add new additions and changes. Fixes will always be in patch.
This contract should allow you to upgrade to new minor and patch versions without breakage or modifications to your existing code. Leave a ticket, if there is breakage, so that it could be fixed.
##LICENSE
Under the MIT License: http://mattn.mit-license.org/2013
# Notes:
# - Minimal appveyor.yml file is an empty file. All sections are optional.
# - Indent each level of configuration with 2 spaces. Do not use tabs!
# - All section names are case-sensitive.
# - Section names should be unique on each level.
version: "1.3.0.{build}-alpha-{branch}"
os: Windows Server 2012 R2
branches:
only:
- master
- v1.2
- v1.1
- v1.0
skip_tags: true
clone_folder: c:\gopath\src\github.com\go-ole\go-ole
environment:
GOPATH: c:\gopath
matrix:
- GOARCH: amd64
GOVERSION: 1.5
GOROOT: c:\go
DOWNLOADPLATFORM: "x64"
install:
- choco install mingw
- SET PATH=c:\tools\mingw64\bin;%PATH%
# - Download COM Server
- ps: Start-FileDownload "https://github.com/go-ole/test-com-server/releases/download/v1.0.2/test-com-server-${env:DOWNLOADPLATFORM}.zip"
- 7z e test-com-server-%DOWNLOADPLATFORM%.zip -oc:\gopath\src\github.com\go-ole\go-ole > NUL
- c:\gopath\src\github.com\go-ole\go-ole\build\register-assembly.bat
# - set
- go version
- go env
- go get -u golang.org/x/tools/cmd/cover
- go get -u golang.org/x/tools/cmd/godoc
- go get -u golang.org/x/tools/cmd/stringer
build_script:
- cd c:\gopath\src\github.com\go-ole\go-ole
- go get -v -t ./...
- go build
- go test -v -cover ./...
# disable automatic tests
test: off
# disable deployment
deploy: off
This diff is collapsed.
// +build !windows
package ole
import (
"time"
"unsafe"
)
// coInitialize initializes COM library on current thread.
//
// MSDN documentation suggests that this function should not be called. Call
// CoInitializeEx() instead. The reason has to do with threading and this
// function is only for single-threaded apartments.
//
// That said, most users of the library have gotten away with just this
// function. If you are experiencing threading issues, then use
// CoInitializeEx().
func coInitialize() error {
return NewError(E_NOTIMPL)
}
// coInitializeEx initializes COM library with concurrency model.
func coInitializeEx(coinit uint32) error {
return NewError(E_NOTIMPL)
}
// CoInitialize initializes COM library on current thread.
//
// MSDN documentation suggests that this function should not be called. Call
// CoInitializeEx() instead. The reason has to do with threading and this
// function is only for single-threaded apartments.
//
// That said, most users of the library have gotten away with just this
// function. If you are experiencing threading issues, then use
// CoInitializeEx().
func CoInitialize(p uintptr) error {
return NewError(E_NOTIMPL)
}
// CoInitializeEx initializes COM library with concurrency model.
func CoInitializeEx(p uintptr, coinit uint32) error {
return NewError(E_NOTIMPL)
}
// CoUninitialize uninitializes COM Library.
func CoUninitialize() {}
// CoTaskMemFree frees memory pointer.
func CoTaskMemFree(memptr uintptr) {}
// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier.
//
// The Programmatic Identifier must be registered, because it will be looked up
// in the Windows Registry. The registry entry has the following keys: CLSID,
// Insertable, Protocol and Shell
// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx).
//
// programID identifies the class id with less precision and is not guaranteed
// to be unique. These are usually found in the registry under
// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of
// "Program.Component.Version" with version being optional.
//
// CLSIDFromProgID in Windows API.
func CLSIDFromProgID(progId string) (*GUID, error) {
return nil, NewError(E_NOTIMPL)
}
// CLSIDFromString retrieves Class ID from string representation.
//
// This is technically the string version of the GUID and will convert the
// string to object.
//
// CLSIDFromString in Windows API.
func CLSIDFromString(str string) (*GUID, error) {
return nil, NewError(E_NOTIMPL)
}
// StringFromCLSID returns GUID formated string from GUID object.
func StringFromCLSID(clsid *GUID) (string, error) {
return "", NewError(E_NOTIMPL)
}
// IIDFromString returns GUID from program ID.
func IIDFromString(progId string) (*GUID, error) {
return nil, NewError(E_NOTIMPL)
}
// StringFromIID returns GUID formatted string from GUID object.
func StringFromIID(iid *GUID) (string, error) {
return "", NewError(E_NOTIMPL)
}
// CreateInstance of single uninitialized object with GUID.
func CreateInstance(clsid *GUID, iid *GUID) (*IUnknown, error) {
return nil, NewError(E_NOTIMPL)
}
// GetActiveObject retrieves pointer to active object.
func GetActiveObject(clsid *GUID, iid *GUID) (*IUnknown, error) {
return nil, NewError(E_NOTIMPL)
}
// VariantInit initializes variant.
func VariantInit(v *VARIANT) error {
return NewError(E_NOTIMPL)
}
// VariantClear clears value in Variant settings to VT_EMPTY.
func VariantClear(v *VARIANT) error {
return NewError(E_NOTIMPL)
}
// SysAllocString allocates memory for string and copies string into memory.
func SysAllocString(v string) *int16 {
u := int16(0)
return &u
}
// SysAllocStringLen copies up to length of given string returning pointer.
func SysAllocStringLen(v string) *int16 {
u := int16(0)
return &u
}
// SysFreeString frees string system memory. This must be called with SysAllocString.
func SysFreeString(v *int16) error {
return NewError(E_NOTIMPL)
}
// SysStringLen is the length of the system allocated string.
func SysStringLen(v *int16) uint32 {
return uint32(0)
}
// CreateStdDispatch provides default IDispatch implementation for IUnknown.
//
// This handles default IDispatch implementation for objects. It haves a few
// limitations with only supporting one language. It will also only return
// default exception codes.
func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (*IDispatch, error) {
return nil, NewError(E_NOTIMPL)
}
// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch.
//
// This will not handle the full implementation of the interface.
func CreateDispTypeInfo(idata *INTERFACEDATA) (*IUnknown, error) {
return nil, NewError(E_NOTIMPL)
}
// copyMemory moves location of a block of memory.
func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) {}
// GetUserDefaultLCID retrieves current user default locale.
func GetUserDefaultLCID() uint32 {
return uint32(0)
}
// GetMessage in message queue from runtime.
//
// This function appears to block. PeekMessage does not block.
func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (int32, error) {
return int32(0), NewError(E_NOTIMPL)
}
// DispatchMessage to window procedure.
func DispatchMessage(msg *Msg) int32 {
return int32(0)
}
func GetVariantDate(value float64) (time.Time, error) {
return time.Now(), NewError(E_NOTIMPL)
}
This diff is collapsed.
This diff is collapsed.
package ole
// OleError stores COM errors.
type OleError struct {
hr uintptr
description string
subError error
}
// NewError creates new error with HResult.
func NewError(hr uintptr) *OleError {
return &OleError{hr: hr}
}
// NewErrorWithDescription creates new COM error with HResult and description.
func NewErrorWithDescription(hr uintptr, description string) *OleError {
return &OleError{hr: hr, description: description}
}
// NewErrorWithSubError creates new COM error with parent error.
func NewErrorWithSubError(hr uintptr, description string, err error) *OleError {
return &OleError{hr: hr, description: description, subError: err}
}
// Code is the HResult.
func (v *OleError) Code() uintptr {
return uintptr(v.hr)
}
// String description, either manually set or format message with error code.
func (v *OleError) String() string {
if v.description != "" {
return errstr(int(v.hr)) + " (" + v.description + ")"
}
return errstr(int(v.hr))
}
// Error implements error interface.
func (v *OleError) Error() string {
return v.String()
}
// Description retrieves error summary, if there is one.
func (v *OleError) Description() string {
return v.description
}
// SubError returns parent error, if there is one.
func (v *OleError) SubError() error {
return v.subError
}
// +build !windows
package ole
// errstr converts error code to string.
func errstr(errno int) string {
return ""
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// This file is here so go get succeeds as without it errors with:
// no buildable Go source files in ...
//
// +build !windows
package oleutil
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment