提交 438174a6 编写于 作者: T Tao Liu

merge from master

......@@ -53,6 +53,7 @@ IF (NOT DEFINED TD_CLUSTER)
SET(TD_MIPS_32 FALSE)
SET(TD_DARWIN_64 FALSE)
SET(TD_WINDOWS_64 FALSE)
SET(TD_WINDOWS_32 FALSE)
SET(TD_PAGMODE_LITE FALSE)
IF (${PAGMODE} MATCHES "lite")
......@@ -127,6 +128,11 @@ IF (NOT DEFINED TD_CLUSTER)
SET(TD_OS_DIR ${TD_COMMUNITY_DIR}/src/os/windows)
ADD_DEFINITIONS(-D_M_X64)
MESSAGE(STATUS "The current platform is Windows 64-bit")
ELSEIF(${CMAKE_SIZEOF_VOID_P} MATCHES 4)
SET(TD_WINDOWS_32 TRUE)
SET(TD_OS_DIR ${TD_COMMUNITY_DIR}/src/os/windows)
#ADD_DEFINITIONS(-D_M_X64)
MESSAGE(STATUS "The current platform is Windows 32-bit")
ELSE ()
MESSAGE(FATAL_ERROR "The current platform is Windows 32-bit, not supported yet")
EXIT ()
......@@ -194,7 +200,7 @@ IF (NOT DEFINED TD_CLUSTER)
link_library(/usr/lib/libargp.a)
ADD_DEFINITIONS(-D_ALPINE)
ENDIF ()
ELSEIF (TD_WINDOWS_64)
ELSEIF (TD_WINDOWS_64 OR TD_WINDOWS_32)
SET(CMAKE_GENERATOR "NMake Makefiles" CACHE INTERNAL "" FORCE)
IF (NOT TD_GODLL)
SET(COMMON_FLAGS "/nologo /WX- /Oi /Oy- /Gm- /EHsc /MT /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Gd /errorReport:prompt /analyze-")
......@@ -235,7 +241,7 @@ IF (NOT DEFINED TD_CLUSTER)
ELSEIF (${CMAKE_BUILD_TYPE} MATCHES "Release")
MESSAGE(STATUS "Build Release Version")
ELSE ()
IF (TD_WINDOWS_64)
IF (TD_WINDOWS_64 OR TD_WINDOWS_32)
SET(CMAKE_BUILD_TYPE "Release")
MESSAGE(STATUS "Build Release Version in Windows as default")
ELSE ()
......@@ -268,7 +274,7 @@ IF (NOT DEFINED TD_CLUSTER)
INSTALL(CODE "MESSAGE(\"make install script: ${TD_MAKE_INSTALL_SH}\")")
INSTALL(CODE "execute_process(COMMAND chmod 777 ${TD_MAKE_INSTALL_SH})")
INSTALL(CODE "execute_process(COMMAND ${TD_MAKE_INSTALL_SH} ${TD_COMMUNITY_DIR} ${PROJECT_BINARY_DIR})")
ELSEIF (TD_WINDOWS_64)
ELSEIF (TD_WINDOWS_64 OR TD_WINDOWS_32)
SET(CMAKE_INSTALL_PREFIX C:/TDengine)
IF (NOT TD_GODLL)
INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/src/connector/go DESTINATION connector)
......@@ -277,6 +283,7 @@ IF (NOT DEFINED TD_CLUSTER)
INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/tests/examples DESTINATION .)
INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/packaging/cfg DESTINATION .)
INSTALL(FILES ${TD_COMMUNITY_DIR}/src/inc/taos.h DESTINATION include)
INSTALL(FILES ${TD_COMMUNITY_DIR}/src/inc/taoserror.h DESTINATION include)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos.lib DESTINATION driver)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos.exp DESTINATION driver)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos.dll DESTINATION driver)
......
## Builder image
#FROM tdengine/tdengine:dev as builder1
FROM golang:latest as builder1
WORKDIR /root
#COPY --from=builder1 /usr/include/taos.h /usr/include/
#COPY --from=builder1 /usr/lib/libtaos.so /usr/lib/libtaos.so
#COPY --from=builder1 /usr/include/taos.h /usr/include/
#COPY --from=builder1 /usr/lib/libtaos.so.1 /usr/lib/
COPY blm_telegraf /root
COPY blm_prometheus /root
COPY TDengine-server-1.6.5.9-Linux-x64.tar.gz /root
COPY go.mod /root
COPY go.sum /root
RUN tar -zxf TDengine-server-1.6.5.9-Linux-x64.tar.gz .
WORKDIR /root/TDengine-server
RUN sh install.sh
#RUN mkdir /usr/lib/ld
WORKDIR /root/blm_telegraf
RUN go build
WORKDIR /root/blm_prometheus
RUN go build
#RUN ln -s /usr/lib/libtaos.so.1 /usr/lib/libtaos.so
FROM centos:7
WORKDIR /root
COPY --from=builder /usr/include/taos.h /usr/include/
COPY --from=builder /usr/lib/libtaos.so /usr/lib/libtaos.so
COPY --from=builder /usr/include/taos.h /usr/include/
COPY --from=builder /usr/lib/libtaos.so.1 /usr/lib/
COPY --from=builder /root/blm_telegraf/blm_telegraf /root/
COPY --from=builder /root/blm_prometheus/blm_prometheus /root/
#RUN git config --global http.sslVerify false
#RUN git config --global http.postbuffer 524288000
#RUN go get -v -u -insecure github.com/taosdata/TDengine/src/connector/go/src/taosSql
#RUN go get -v -u -insecure github.com/gogo/protobuf/proto
#RUN go get -v -u -insecure github.com/golang/snappy
#RUN go get -v -u -insecure github.com/prometheus/common/model
#RUN go get -v -u -insecure github.com/prometheus/prometheus/prompb
#RUN go get github.com/taosdata/driver-go/taosSql
## Builder image
FROM tdengine/tdengine:1.6.5.9 as builder1
FROM golang:latest as builder
WORKDIR /root
COPY --from=builder1 /usr/include/taos.h /usr/include/
COPY --from=builder1 /usr/lib/libtaos.so /usr/lib/libtaos.so
COPY --from=builder1 /usr/include/taos.h /usr/include/
COPY --from=builder1 /usr/lib/libtaos.so.1 /usr/lib/
COPY blm_prometheus /root/blm_prometheus
COPY go.mod /root
COPY go.sum /root
WORKDIR /root/blm_prometheus
RUN go build
FROM centos:7
WORKDIR /root
COPY --from=builder1 /usr/include/taos.h /usr/include/
COPY --from=builder1 /usr/lib/libtaos.so.1 /usr/lib/
RUN ln -s /usr/lib/libtaos.so.1 /usr/lib/libtaos.so
COPY --from=builder /root/blm_prometheus /root
ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib"
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US:en
ENV LC_ALL=en_US.UTF-8
#RUN git config --global http.sslVerify false
#RUN git config --global http.postbuffer 524288000
VOLUME [ "/var/lib/taos", "/var/log/taos","/etc/taos/" ]
ENTRYPOINT [ "/root/blm_prometheus" ]
## Builder image
FROM tdengine/tdengine:1.6.5.9 as builder1
FROM golang:latest as builder
WORKDIR /root
COPY --from=builder1 /usr/include/taos.h /usr/include/
COPY --from=builder1 /usr/lib/libtaos.so /usr/lib/libtaos.so
COPY --from=builder1 /usr/include/taos.h /usr/include/
COPY --from=builder1 /usr/lib/libtaos.so.1 /usr/lib/
COPY blm_telegraf /root/blm_telegraf
COPY go.mod /root
COPY go.sum /root
WORKDIR /root/blm_telegraf
RUN ls
RUN go build
FROM centos:7
WORKDIR /root
COPY --from=builder1 /usr/include/taos.h /usr/include/
COPY --from=builder1 /usr/lib/libtaos.so.1 /usr/lib/
RUN ln -s /usr/lib/libtaos.so.1 /usr/lib/libtaos.so
COPY --from=builder /root/blm_telegraf /root
ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib"
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US:en
ENV LC_ALL=en_US.UTF-8
VOLUME [ "/var/lib/taos", "/var/log/taos","/etc/taos/" ]
ENTRYPOINT [ "/root/blm_telegraf"]
## Builder image
FROM tdengine/tdengine:dev as builder1
FROM tdengine/bailongma:dev as builder
COPY server.go /root/blm_prometheus/
WORKDIR /root/blm_prometheus
# build enterprise version
RUN go build
# # build community version
# RUN cmake .. -DVERSION=lite && cmake --build .
## Target image
FROM centos:7
WORKDIR /root/
# COPY --from=builder /root/build/build/lib/libtaos.so /usr/lib/libtaos.so.1
# RUN ln -s /usr/lib/libtaos.so.1 /usr/lib/libtaos.so
COPY --from=builder1 /usr/include/taos.h /usr/include/
COPY --from=builder1 /usr/lib/libtaos.so.1 /usr/lib/libtaos.so.1
RUN ln -s /usr/lib/libtaos.so.1 /usr/lib/libtaos.so
COPY --from=builder /root/blm_prometheus/blm_prometheus .
#COPY community/packaging/cfg/taos.cfg /etc/taos/
ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib"
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US:en
ENV LC_ALL=en_US.UTF-8
EXPOSE 10203
VOLUME [ "/var/lib/taos", "/var/log/taos","/etc/taos" ]
ENTRYPOINT [ "/root/blm_prometheus" ]
\ No newline at end of file
# Prometheus Remote Write Adapter for TDengine
This is an adapter to support Prometheus remote write into TDengine.
## Prerequisite
before running the software, you need to install the `golang-1.10` or later version in your environment and install [TDengine][] so the program can use the lib of TDengine.
To use it:
```
go build
```
During the go build process, there maybe some errors arised because of lacking some needed packages. You can use `go get` the package to solve it
```
go get github.com/gogo/protobuf/proto
go get github.com/golang/snappy
go get github.com/prometheus/common/model
go get github.com/taosdata/TDengine/src/connector/go/src/taosSql
go get github.com/prometheus/prometheus/prompb
```
After successful build, there will be a blm_prometheus in the same directory.
## Running in background
Using following command to run the program in background
```
nohup ./blm_prometheus --tdengine-ip 112.102.3.69 --batch-size 80 --http-workers 2 --sql-workers 2 --dbname prometheus --port 1234 > /dev/null 2>&1 &
```
There are several options can be set:
```sh
--tdengine-ip
set the IP of TDengine for example "192.168.0.1"
--tdengine-name
set the domain name of TDengine, then blm-prometheus can lookup the ip address of TDengine.
--tdengine-api-port
set the restful API port of TDengine. blm-prometheus will query the table schema info from TDengine to keep meta info synchronized. Default is 6020
--batch-size
set the size of how many records in one SQL cmd line writing into TDengine. There is a limitation that TDengine could only accept SQL line small than 64000 bytes, so usually the batch size should not exceed 200. Default is 100.
--http-workers
set the number of workers who process the HTTP request. default is 10
--sql-workers
set the number of workers who process the database request. default is 10
--dbname
set the database name in TDengine, if not exists, a database will be created after this dbname. default is "prometheus".
--dbuser
set the user name that have the right to access the TDengine. default is "root"
--dbpassword
set the password of dbuser. default is "taosdata"
--port
set the port that prometheus configuration remote_write. as showed above, in the prometheus.yaml. default is 10203
```
## Start prometheus
Add the following to your prometheus's configuration `prometheus.yml` :
```yaml
remote_write:
- url: "http://localhost:1234/receive"
```
Then start Prometheus:
```
prometheus
```
Then you can check the TDengine if there is super table and tables.
## Check the TDengine tables and datas
Use the taos client shell to query the result.
```
Welcome to the TDengine shell from linux, client version:1.6.4.0 server version:1.6.4.0
Copyright (c) 2017 by TAOS Data, Inc. All rights reserved.
This is the trial version and will expire at 2019-12-11 14:25:31.
taos> use prometheus;
Database changed.
taos> show stables;
name | created_time |columns| tags | tables |
====================================================================================================================
prometheus_sd_kubernetes_cache_watch_events_sum | 19-11-15 17:45:07.594| 2| 3| 1|
prometheus_sd_kubernetes_cache_watch_events_count | 19-11-15 17:45:07.596| 2| 3| 1|
prometheus_sd_kubernetes_cache_watches_total | 19-11-15 17:45:07.598| 2| 3| 1|
prometheus_sd_kubernetes_events_total | 19-11-15 17:45:07.600| 2| 5| 15|
prometheus_target_scrape_pool_reloads_total | 19-11-15 17:45:07.672| 2| 3| 1|
prometheus_sd_received_updates_total | 19-11-15 17:45:07.674| 2| 4| 1|
prometheus_target_scrape_pool_reloads_failed_total | 19-11-15 17:45:07.730| 2| 3| 1|
prometheus_sd_updates_total | 19-11-15 17:45:07.732| 2| 4| 1|
prometheus_target_scrape_pool_sync_total | 19-11-15 17:45:07.734| 2| 4| 1|
......
go_memstats_gc_cpu_fraction | 19-11-15 17:45:06.599| 2| 3| 1|
Query OK, 211 row(s) in set (0.004891s)
taos> select * from prometheus_sd_updates_total;
......
19-11-16 14:24:00.271| 1.000000000|localhost:9090 |prometheus |codelab-monitor |scrape |
19-11-16 14:24:05.271| 1.000000000|localhost:9090 |prometheus |codelab-monitor |scrape |
Query OK, 3029 row(s) in set (0.060828s)
```
## Support Kubernates liveness probe
The blm_prometheus support the liveness probe.
When the service is running, GET the url`http://ip:port/health` will return 200 OK response which means the service is running healthy. If no response, means the service is dead and need to restart it.
## Limitations
The TDengine limits the length of super table name, so if the name of prometheus metric exceeds 60 byte, it will be truncated to first 60 bytes. And the length of label name is limited within 50 byte.
[TDengine]:https://www.github.com/Taosdata/TDengine
\ No newline at end of file
此差异已折叠。
## Builder image
FROM tdengine/tdengine:dev as builder1
FROM tdengine/bailongma:dev as builder
COPY server.go /root/blm_telegraf/
WORKDIR /root/blm_telegraf
RUN go build
# # build community version
# RUN cmake .. -DVERSION=lite && cmake --build .
## Target image
FROM centos:7
WORKDIR /root
# COPY --from=builder /root/build/build/lib/libtaos.so /usr/lib/libtaos.so.1
# RUN ln -s /usr/lib/libtaos.so.1 /usr/lib/libtaos.so
COPY --from=builder1 /usr/include/taos.h /usr/include/
COPY --from=builder1 /usr/lib/libtaos.so /usr/lib/libtaos.so
RUN ln -s /usr/lib/libtaos.so /usr/lib/libtaos.so.1
COPY --from=builder /root/blm_telegraf/blm_telegraf .
#COPY community/packaging/cfg/taos.cfg /etc/taos/
ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib"
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US:en
ENV LC_ALL=en_US.UTF-8
EXPOSE 10202
VOLUME [ "/var/lib/taos", "/var/log/taos" ]
ENTRYPOINT [ "/root/blm_telegraf","--host" ]
\ No newline at end of file
# API for Telegraf
This is an API to support Telegraf writing data into TDengine.
## prerequisite
before running the software, you need to install the `golang-1.10` or later version in your environment and install [TDengine][] so the program can use the lib of TDengine.
To use it:
```
go build
```
During the go build process, there maybe some errors arised because of lacking some needed packages. You can use `go get` the package to solve it
```
go get github.com/taosdata/TDengine/src/connector/go/src/taosSql
```
After successful build, there will be a blm_telegraf in the same directory.
## Running in background
Using following command to run the program in background
```
nohup ./blm_telegraf --host 112.102.3.69:0 --batch-size 200 --http-workers 2 --sql-workers 2 --dbname telegraf --port 1234 > /dev/null 2>&1 &
```
The API url is `http://ipaddress:port/telegraf`
There are several options can be set:
```
--host
set the host of TDengine, IP:port, for example "192.168.0.1:0"
--batch-size
set the size of how many records in one SQL cmd line writing into TDengine. There is a limitation that TDengine could only accept SQL line small than 64000 bytes, so usually the batch size should not exceed 800. Default is 10.
--http-workers
set the number of workers who process the HTTP request. default is 10
--sql-workers
set the number of workers who process the database request. default is 10
--dbname
set the database name in TDengine, if not exists, a database will be created after this dbname. default is "telegraf".
--dbuser
set the user name that have the right to access the TDengine. default is "root"
--dbpassword
set the password of dbuser. default is "taosdata"
--port
set the port that prometheus configuration remote_write. as showed above, in the prometheus.yaml
```
## Configure the Telegraf
To write into blm_telegraf API, you should configure the telegraf as below
In the telegraf configuration file, output plugin part:
1. telegraf out put plugin setup:
Set the url to the blm_telegraf API
Set the data format as "json"
Set the json timstamp units as "1ms"
```toml
[[outputs.http]]
# ## URL is the address to send metrics to
url = "http://114.116.124.178:8081/telegraf"
data_format = "json"
json_timestamp_units = "1ms"
```
In the Agent part, the hostname should be unique among all the telegraf which report to the TDengine.
```toml
# Configuration for telegraf agent
[agent]
## Default data collection interval for all inputs
interval = "5s"
...
## Override default hostname, if empty use os.Hostname()
hostname = "testhost1"
```
Then start telegraf:
```sh
telegraf --config xxx.conf
```
Then you can check the TDengine if there is super table and tables.
## Check the TDengine tables and datas
Use the taos client shell to query the result.
```
Welcome to the TDengine shell from linux, client version:1.6.4.0 server version:1.6.4.0
Copyright (c) 2017 by TAOS Data, Inc. All rights reserved.
This is the trial version and will expire at 2019-12-11 14:25:31.
taos> use prometheus;
Database changed.
taos> show stables;
name | created_time |columns| tags | tables |
====================================================================================================================
system | 19-11-22 21:48:10.205| 2| 3| 12|
system_str | 19-11-22 21:48:10.205| 2| 3| 2|
cpu | 19-11-22 21:48:10.225| 2| 4| 200|
cpu_str | 19-11-22 21:48:10.226| 2| 4| 0|
processes | 19-11-22 21:48:10.230| 2| 3| 16|
processes_str | 19-11-22 21:48:10.230| 2| 3| 0|
disk | 19-11-22 21:48:10.233| 2| 7| 357|
disk_str | 19-11-22 21:48:10.234| 2| 7| 0|
diskio | 19-11-22 21:48:10.247| 2| 4| 72|
diskio_str | 19-11-22 21:48:10.248| 2| 4| 0|
swap | 19-11-22 21:48:10.254| 2| 3| 7|
swap_str | 19-11-22 21:48:10.255| 2| 3| 0|
mem | 19-11-22 21:48:10.272| 2| 3| 61|
mem_str | 19-11-22 21:48:10.272| 2| 3| 0|
Query OK, 14 row(s) in set (0.000733s)
taos> select * from mem;
......
19-11-23 14:19:11.000| 0.000000000|testhost1 |1.202.240.226 |huge_pages_free |
19-11-23 14:19:16.000| 0.000000000|testhost1 |1.202.240.226 |huge_pages_free |
19-11-23 14:19:21.000| 0.000000000|testhost1 |1.202.240.226 |huge_pages_free |
19-11-23 14:19:26.000| 0.000000000|testhost1 |1.202.240.226 |huge_pages_free |Query OK, 3029 row(s) in set (0.060828s)
```
## Support Kubernates liveness probe
The blm_telegraf support the liveness probe.
When the service is running, GET the url`http://ip:port/health` will return 200 OK response which means the service is running healthy. If no response, means the service is dead and need to restart it.
## Limitations
The TDengine limits the length of super table name, so if the name of Telegraf measurement name exceeds 60 byte, it will be truncated to first 60 bytes. And the length of tags name is limited within 50 byte.
[TDengine]:https://www.github.com/Taosdata/TDengine
\ No newline at end of file
# Telegraf写入TDengine的API程序
刚刚开发完Telegraf写入TDengine的APi程序,本文总结一下程序的设计思路,并在此基础上提出一种schemaless的写入TDengine的适配方法和对应的查询思路。
## Telegraf数据分析
Telegraf采集节点的数据后,按照数据的格式为measurement加上一系列的tags,再加上一系列的fields和timestamp,组成一条记录发出。
```
cpu,cpu=cpu-total,host=liutaodeMacBook-Pro.local usage_irq=0,usage_guest=0,usage_guest_nice=0,usage_iowait=0,usage_softirq=0,usage_steal=0,usage_user=10.55527763881941,usage_system=3.5767883941970986,usage_idle=85.86793396698349,usage_nice=0 1571663200000000000
```
上面是一条按照influxdb格式输出的记录,第一个字段是measurement, 然后接着两个tags, tags后面的空格来作为tags和fields的分隔;fields和timestamp之间也是用空格分隔。
```json
{
"fields":{
"usage_guest":0,
"usage_guest_nice":0,
"usage_idle":87.73726273726274,
"usage_iowait":0,
"usage_irq":0,
"usage_nice":0,
"usage_softirq":0,
"usage_steal":0,
"usage_system":2.6973026973026974,
"usage_user":9.565434565434565
},
"name":"cpu",
"tags":{
"cpu":"cpu-total",
"host":"liutaodeMacBook-Pro.local"
},
"timestamp":1571665100
}
```
如果按json格式输出,则一条json格式的记录如上。
上面的数据个看上去跟TDengine的记录格式十分类似,很自然的我们可以把name作为超级表名,tags作为tags,fields作为values,timestamp作为timestamp,对应上TDengine的数据格式,照理说应该非常好写入TDengine。但实际过程中发现telegraf在输出数据时,经常会遇到一个问题,就是name,tags格式一样的情况下,fields的格式不一样,fields里的名字,数量,都可能变化。这种变化不是随意变化,可能是两三种组合的变化。比如如下情况
```
swap,host=testhost1 out=0i,in=0i 1574663615000000000
swap,host=testhost1 total=4294967296i,used=3473670144i,free=821297152i,used_percent=80.877685546875 1574663615000000000
```
同一个时间点来的两条记录,name都是swap,tag都是host,但fields却完全不相同。
再比如
```
system,host=testhost1 uptime_format="5 days, 1:07" 1574663615000000000
system,host=testhost1 uptime=436070i 1574663615000000000
system,host=testhost1 load15=5.9521484375,n_cpus=4i,n_users=6i,load1=3.17138671875,load5=6.462890625 1574663615000000000
```
同一时间点来的三条记录,name都是system,tags都是host,但fields完全不同。
如果以name作为TDengine的超级表名,就会面临到表格的结构发生变化。并且,由于influxdb是schemaless的设计,他们能够很好的处理这种变化,不管fields如何变化,都能顺利写入。因此,很难保证telegraf后续产生的数据,fields发生会怎么变化。如何设计TDengine的存储表结构,是一个问题。
## TDengine的表结构设计思路
面对这种数据来源,我们一般可以用两种设计思路:
### 提前设计好超级表,包含所有fields, 这种就是schema方式
一种,是在对数据的行为有充分的了解后,提前设计好TDengine的表格式,将所有可能变化的fields都放到values中,提前创建好超级表;然后在插入数据时,把同一时间戳的所有fields都收集齐后,再组装成一条TDengine的SQL记录,写入TDengine。这种,是我们当前通常用到的方法,可以成为schema方式的写入。这种方法的优点是,比较符合我们TDengine的设计假设,values有多列,写入性能相对高一些。但也有明显的缺点,需要提前对数据做大量的分析,确定每个测量的格式,手动来写schema配置文件或手动在TDengine客户端创建超级表,对于节点很多的监控,这种设计会带来较大的工作量。
### 不提前设计超级表,设计一种建表规则,根据来的数据自动写入,类似influxdb的schemaless的方式
另外一种,就是根据收到的数据自动建表,只要符合name,tags,fields,timestamp的格式,都能顺利写的创建TDengine的表,并写入TDengine。本程序就是采用这种思路,将每一个fields单独拆开,和name,tags组合起来,形成一个单列的表。这样的超级表符合任何fields,对于任意fields都可以顺利写入。下面将沿着这个设计思路继续展开。
## 超级表
本程序以收到的原始数据name作为超级表名,原始数据中的tags作为tags,同时,额外增加两个tag,一个是发来请求的源IP,用来区分设备;另一个是field,这个tag的值是原始数据中fields的名称,用来表明这个超级表存的是哪个指标。以上面的system这个原始数据为例,则超级表结构为
```toml
stablename = system
tags = ['host','srcip','field']
values = ['timestamp','value']
```
其中,tags的类型都为binary(50),长度超过50的标签值都截断为50;field这个标签则的可能值则为
```toml
field : ['uptime_format','uptime','load15','n_cpus','n_users','load1','load5']
```
## value的类型
由于无法预知数据的类型,以及简化程序实现,我们将value的类型分成两类,一类是数值型,统一用double来存储;一类是字符串,统一用binary(256)的类型来存。由于所有field都要用同一个超级表来存,因此我一开始就为每个name创建了两个超级表,一个是数值型的超级表,表名就是name;另一个是字符串型的超级表,表名是name加上_str后缀。然后根据field的数据类型,如果是数值型,就用数值型的超级表来创建表;如果是字符串型的,就用name_str的超级表来创建表。
因此,超级表创建的时候会创建两倍的数据量
```
name | created_time |columns| tags | tables |
====================================================================================================================
system | 19-11-22 21:48:10.205| 2| 3| 12|
system_str | 19-11-22 21:48:10.205| 2| 3| 2|
cpu | 19-11-22 21:48:10.225| 2| 4| 200|
cpu_str | 19-11-22 21:48:10.226| 2| 4| 0|
processes | 19-11-22 21:48:10.230| 2| 3| 16|
processes_str | 19-11-22 21:48:10.230| 2| 3| 0|
disk | 19-11-22 21:48:10.233| 2| 7| 357|
disk_str | 19-11-22 21:48:10.234| 2| 7| 0|
diskio | 19-11-22 21:48:10.247| 2| 4| 72|
diskio_str | 19-11-22 21:48:10.248| 2| 4| 0|
swap | 19-11-22 21:48:10.254| 2| 3| 7|
swap_str | 19-11-22 21:48:10.255| 2| 3| 0|
mem | 19-11-22 21:48:10.272| 2| 3| 61|
mem_str | 19-11-22 21:48:10.272| 2| 3| 0|
Query OK, 14 row(s) in set (0.000588s)
```
因此查询的时候,需要根据查询值的类型,选择不同的超级表来查询
比如,对于数值类型,查询n_cpus值的语句为
```sql
Select * from system where field = "n_cpus";
```
对于字符串类型,查询uptime_format的值的语句为
```sql
Select * from system_str where field = "uptime_format";
```
## 表的创建
对于每个field,程序为它创建了一个表,表名规则如下:
将原始数据的所有tags值加上源ip加上field的名称,组成一个长的字符串,然后进行MD5计算,输出的结果加上MD5_作为前缀,形成表名。
这种规则,确保了只要数据的tags等特征不变,表就不会发生变化。
```
...
md5_b26d30c2e07529ac309d836b3b222f15 | 19-11-24 21:34:35.830| 2|processes |
md5_08147d718d4961368155f90432eab536 | 19-11-22 21:48:10.748| 2|disk |
md5_105158abfca0bbf0d932cc74bfc7e136 | 19-11-24 21:34:35.846| 2|mem |
md5_e6842b5c6b9744b7d5ce3510a4d54c98 | 19-11-24 21:34:35.874| 2|disk |
md5_285fd02686e0bfee76d22505dd29f14c | 19-11-22 21:48:11.509| 2|disk |
md5_9317870bb00109353f6aef58ee2ee9e9 | 19-11-24 21:34:35.919| 2|cpu |
Query OK, 727 row(s) in set (0.020405s)
```
因此在数据插入时,只要根据tags值和IP和field的名称,就能计算出表名,直接插入该表。
查询时,可以用超级表加上tag值和field值来查询,也很清晰便利。
因此,数值写入自动生成的sql语句如下:
```sql
insert into md5_285fd02686e0bfee76d22505dd29f14c values(1574663615000,375.2023040);
```
其中表名md5_285fd02686e0bfee76d22505dd29f14c是自动根据数据特征计算出来的,无需人工输入。
而查询则可以通过超级表来查询
## Schemaless写入方法
基于上面的实现,后续我们可以确定一个写入的语法,就可以不用提前设定schema,而根据接收到的数据,自动创建超级表,表,方便的写入TDengine了。
沿用现有的json格式,或者参考influxdb的语法,确定一个写入的语法,就可以实现influxdb的schemaless写入的能力。
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* 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.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"container/list"
"crypto/md5"
"database/sql"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"sort"
"strconv"
"strings"
"sync"
"time"
_ "github.com/taosdata/driver-go/taosSql"
)
type metric struct {
Fields map[string]interface{}
Name string
Tags map[string]string
TimeStamp int64
}
type Metrics struct {
Metrics []metric
HostIP string
}
var (
daemonUrl string
httpworkers int
sqlworkers int
batchSize int
buffersize int
dbname string
dbuser string
dbpassword string
rwport string
debugprt int
taglen int
)
type nametag struct {
tagmap map[string]string
taglist *list.List
}
// Global vars
var (
bufPool sync.Pool
batchChans []chan string //multi table one chan
nodeChans []chan Metrics //multi node one chan
inputDone chan struct{}
//workersGroup sync.WaitGroup
reportTags [][2]string
reportHostname string
taosDriverName string = "taosSql"
IsSTableCreated sync.Map
IsTableCreated sync.Map
taglist *list.List
nametagmap map[string]nametag
tagstr string
blmLog *log.Logger
logNameDefault string = "/var/log/taos/blm_telegraf.log"
)
var scratchBufPool = &sync.Pool{
New: func() interface{} {
return make([]byte, 0, 1024)
},
}
// Parse args:
func init() {
flag.StringVar(&daemonUrl, "host", "", "TDengine host.")
flag.IntVar(&batchSize, "batch-size", 10, "Batch size (input items).")
flag.IntVar(&httpworkers, "http-workers", 10, "Number of parallel http requests handler .")
flag.IntVar(&sqlworkers, "sql-workers", 10, "Number of parallel sql handler.")
flag.StringVar(&dbname, "dbname", "telegraf", "Database name where to store metrics")
flag.StringVar(&dbuser, "dbuser", "root", "User for host to send result metrics")
flag.StringVar(&dbpassword, "dbpassword", "taosdata", "User password for Host to send result metrics")
flag.StringVar(&rwport, "port", "10202", "remote write port")
flag.IntVar(&debugprt, "debugprt", 0, "if 0 not print, if 1 print the sql")
flag.IntVar(&taglen, "tag-length", 30, "the max length of tag string")
flag.IntVar(&buffersize, "buffersize", 100, "the buffer size of metrics received")
flag.Parse()
daemonUrl = daemonUrl + ":0"
nametagmap = make(map[string]nametag)
fmt.Print("host: ")
fmt.Print(daemonUrl)
fmt.Print(" port: ")
fmt.Print(rwport)
fmt.Print(" database: ")
fmt.Print(dbname)
tagstr = fmt.Sprintf(" binary(%d)", taglen)
logFile, err := os.OpenFile(logNameDefault, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
blmLog = log.New(logFile, "", log.LstdFlags)
blmLog.SetPrefix("BLM_TLG")
blmLog.SetFlags(log.LstdFlags | log.Lshortfile)
}
func main() {
for i := 0; i < httpworkers; i++ {
nodeChans = append(nodeChans, make(chan Metrics, buffersize))
}
createDatabase(dbname)
for i := 0; i < httpworkers; i++ {
//workersGroup.Add(1)
go NodeProcess(i)
}
for i := 0; i < sqlworkers; i++ {
batchChans = append(batchChans, make(chan string, batchSize))
}
for i := 0; i < sqlworkers; i++ {
//workersGroup.Add(1)
go processBatches(i)
}
http.HandleFunc("/telegraf", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusAccepted)
addr := strings.Split(r.RemoteAddr, ":")
idx := TAOShashID([]byte(addr[0]))
reqBuf, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
r.Body.Close()
var req Metrics
if err := json.Unmarshal(reqBuf, &req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
req.HostIP = addr[0]
nodeChans[idx%httpworkers] <- req
r.Body.Close()
})
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
})
blmLog.Fatal(http.ListenAndServe(":"+rwport, nil))
}
func TAOShashID(ba []byte) int {
var sum int = 0
for i := 0; i < len(ba); i++ {
sum += int(ba[i] - '0')
}
return sum
}
func TAOSstrCmp(a string, b string) bool {
//return if a locates before b in a dictrionary.
for i := 0; i < len(a) && i < len(b); i++ {
if int(a[i]-'0') > int(b[i]-'0') {
return false
} else if int(a[i]-'0') < int(b[i]-'0') {
return true
}
}
if len(a) > len(b) {
return false
} else {
return true
}
}
func NodeProcess(workerid int) error {
for req := range nodeChans[workerid] {
ProcessReq(req)
}
return nil
}
func OrderInsert(ts int64, l *list.List) {
e := l.Front()
if e == nil {
l.PushFront(ts)
return
}
for e = l.Front(); e != nil; e = e.Next() {
if e.Value.(int64) < ts {
continue
} else {
l.InsertBefore(ts, e)
return
}
}
}
func OrderInsertS(s string, l *list.List) {
e := l.Front()
if e == nil {
l.PushFront(s)
return
}
for e = l.Front(); e != nil; e = e.Next() {
str := e.Value.(string)
if TAOSstrCmp(str, s) {
continue
} else {
l.InsertBefore(s, e)
return
}
}
l.PushBack(s)
return
}
func ProcessReq(req Metrics) error {
tsmap := make(map[int64]map[string][]metric)
tslist := list.New()
addr := req.HostIP
var lastTs int64 = 0
for i := 0; i < len(req.Metrics); i++ {
m := req.Metrics[i]
if tsmap[m.TimeStamp] == nil {
tsmap[m.TimeStamp] = make(map[string][]metric)
}
mp := tsmap[m.TimeStamp]
mp[m.Name] = append(mp[m.Name], m)
if lastTs != m.TimeStamp { //there is still some case that will make mistake, when the timestamp is totally out of order. but right now just forget it.
OrderInsert(m.TimeStamp, tslist)
}
lastTs = m.TimeStamp
}
for e := tslist.Front(); e != nil; e = e.Next() {
namemap, ok := tsmap[e.Value.(int64)]
if ok {
for _, v := range namemap {
ProcessData(v, dbname, addr)
}
} else {
info := fmt.Sprintf("ProcessReq: cannot retrieve map")
panic(info)
}
}
return nil
}
func SerilizeTDengine(m metric, dbn string, hostip string, taglist *list.List, db *sql.DB) error {
var tbna []string
for _, v := range m.Tags {
tbna = append(tbna, v)
}
sort.Strings(tbna)
tbn := strings.Join(tbna, "") // Go map 遍历结果是随机的,必须排下序
for k, v := range m.Fields {
s := m.Name + tbn + hostip + k
//fmt.Print(s)
s = "MD5_" + md5V2(s)
_, ok := IsTableCreated.Load(s)
if !ok {
var sqlcmd string
switch v.(type) {
case string:
sqlcmd = "create table if not exists " + s + " using " + m.Name + "_str tags("
default:
sqlcmd = "create table if not exists " + s + " using " + m.Name + " tags("
}
for e := taglist.Front(); e != nil; e = e.Next() {
tagvalue, has := m.Tags[e.Value.(string)]
if len(tagvalue) >= 60 {
tagvalue = tagvalue[:59]
}
if has {
sqlcmd = sqlcmd + "\"" + tagvalue + "\","
} else {
sqlcmd = sqlcmd + "null,"
}
}
sqlcmd = sqlcmd + "\"" + hostip + "\"," + "\"" + k + "\")\n"
execSql(dbn, sqlcmd, db)
IsTableCreated.Store(s, true)
}
idx := TAOShashID([]byte(s))
sqlcmd := " " + s + " values("
tls := strconv.FormatInt(m.TimeStamp, 10)
switch v.(type) {
case string:
sqlcmd = sqlcmd + tls + ",\"" + v.(string) + "\")"
case int64:
sqlcmd = sqlcmd + tls + "," + strconv.FormatInt(v.(int64), 10) + ")"
case float64:
sqlcmd = sqlcmd + tls + "," + strconv.FormatFloat(v.(float64), 'E', -1, 64) + ")"
default:
panic("Checktable error value type")
}
batchChans[idx%sqlworkers] <- sqlcmd
//execSql(dbn,sqlcmd)
}
return nil
}
func ProcessData(ts []metric, dbn string, hostip string) error {
db, err := sql.Open(taosDriverName, dbuser+":"+dbpassword+"@/tcp("+daemonUrl+")/"+dbname)
if err != nil {
blmLog.Fatalf("Open database error: %s\n", err)
}
defer db.Close()
schema, ok := IsSTableCreated.Load(ts[0].Name)
if !ok {
var nt nametag
nt.taglist = list.New()
nt.tagmap = make(map[string]string)
IsSTableCreated.Store(ts[0].Name, nt)
tagmap := nt.tagmap
taglist := nt.taglist
for i := 0; i < len(ts); i++ {
for k, _ := range ts[i].Tags {
_, ok := tagmap[k]
if !ok {
taglist.PushBack(k)
tagmap[k] = "y"
}
}
}
var sqlcmd string
sqlcmd = "create table if not exists " + ts[0].Name + " (ts timestamp, value double) tags("
sqlcmd1 := "create table if not exists " + ts[0].Name + "_str (ts timestamp, value binary(256)) tags("
for e := taglist.Front(); e != nil; e = e.Next() {
sqlcmd = sqlcmd + e.Value.(string) + tagstr + ","
sqlcmd1 = sqlcmd1 + e.Value.(string) + tagstr + ","
}
sqlcmd = sqlcmd + "srcip binary(20), field binary(40))\n"
sqlcmd1 = sqlcmd1 + "srcip binary(20), field binary(40))\n"
execSql(dbn, sqlcmd, db)
execSql(dbn, sqlcmd1, db)
for i := 0; i < len(ts); i++ {
SerilizeTDengine(ts[i], dbn, hostip, taglist, db)
}
return nil
}
nt := schema.(nametag)
tagmap := nt.tagmap
taglist := nt.taglist
var sqlcmd, sqlcmd1 string
for i := 0; i < len(ts); i++ {
for k, _ := range ts[i].Tags {
_, ok := tagmap[k]
if !ok {
sqlcmd = sqlcmd + "alter table " + ts[0].Name + " add tag " + k + tagstr + "\n"
sqlcmd1 = sqlcmd1 + "alter table " + ts[0].Name + "_str add tag " + k + tagstr + "\n"
taglist.PushBack(k)
tagmap[k] = "y"
}
}
}
execSql(dbn, sqlcmd, db)
execSql(dbn, sqlcmd1, db)
for i := 0; i < len(ts); i++ {
SerilizeTDengine(ts[i], dbn, hostip, taglist, db)
}
return nil
}
func createDatabase(dbname string) {
db, err := sql.Open(taosDriverName, dbuser+":"+dbpassword+"@/tcp("+daemonUrl+")/")
if err != nil {
log.Fatalf("Open database error: %s\n", err)
}
defer db.Close()
sqlcmd := fmt.Sprintf("create database if not exists %s", dbname)
_, err = db.Exec(sqlcmd)
sqlcmd = fmt.Sprintf("use %s", dbname)
_, err = db.Exec(sqlcmd)
checkErr(err)
return
}
func execSql(dbname string, sqlcmd string, db *sql.DB) {
if len(sqlcmd) < 1 {
return
}
_, err := db.Exec(sqlcmd)
if err != nil {
var count int = 2
for {
if err != nil && count > 0 {
<-time.After(time.Second * 1)
_, err = db.Exec(sqlcmd)
count--
} else {
if err != nil {
blmLog.Printf("execSql Error: %s sqlcmd: %s\n", err, sqlcmd)
return
}
break
}
}
}
return
}
func checkErr(err error) {
if err != nil {
blmLog.Println(err)
}
}
func md5V2(str string) string {
data := []byte(str)
has := md5.Sum(data)
md5str := fmt.Sprintf("%x", has)
return md5str
}
func processBatches(iworker int) {
var i int
db, err := sql.Open(taosDriverName, dbuser+":"+dbpassword+"@/tcp("+daemonUrl+")/"+dbname)
if err != nil {
blmLog.Printf("processBatches Open database error: %s\n", err)
var count int = 5
for {
if err != nil && count > 0 {
<-time.After(time.Second * 1)
_, err = sql.Open(taosDriverName, dbuser+":"+dbpassword+"@/tcp("+daemonUrl+")/"+dbname)
count--
} else {
if err != nil {
blmLog.Printf("processBatches Error: %s open database\n", err)
return
}
break
}
}
}
defer db.Close()
sqlcmd := make([]string, batchSize+1)
i = 0
sqlcmd[i] = "Insert into"
i++
for onepoint := range batchChans[iworker] {
sqlcmd[i] = onepoint
i++
if i > batchSize {
i = 1
_, err := db.Exec(strings.Join(sqlcmd, ""))
if err != nil {
var count int = 2
for {
if err != nil && count > 0 {
<-time.After(time.Second * 1)
_, err = db.Exec(strings.Join(sqlcmd, ""))
count--
} else {
if err != nil {
blmLog.Printf("Error: %s sqlcmd: %s\n", err, strings.Join(sqlcmd, ""))
}
break
}
}
}
}
}
if i > 1 {
i = 1
_, err := db.Exec(strings.Join(sqlcmd, ""))
if err != nil {
var count int = 2
for {
if err != nil && count > 0 {
<-time.After(time.Second * 1)
_, err = db.Exec(strings.Join(sqlcmd, ""))
count--
} else {
if err != nil {
blmLog.Printf("Error: %s sqlcmd: %s\n", err, strings.Join(sqlcmd, ""))
}
break
}
}
}
}
//workersGroup.Done()
}
module github.com/taosdata/TDengine/bailongma
go 1.14
require (
github.com/gogo/protobuf v1.3.1
github.com/golang/snappy v0.0.1
github.com/grpc-ecosystem/grpc-gateway v1.14.5 // indirect
github.com/prometheus/common v0.9.1
github.com/prometheus/prometheus v2.5.0+incompatible
github.com/taosdata/driver-go v0.0.0-20200311072652-8c58c512b6ac
google.golang.org/genproto v0.0.0-20200507105951-43844f6eee31 // indirect
google.golang.org/grpc v1.29.1 // indirect
)
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/grpc-ecosystem/grpc-gateway v1.14.5 h1:aiLxiiVzAXb7wb3lAmubA69IokWOoUNe+E7TdGKh8yw=
github.com/grpc-ecosystem/grpc-gateway v1.14.5/go.mod h1:UJ0EZAp832vCd54Wev9N1BMKEyvcZ5+IM0AwDrnlkEc=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/prometheus v1.8.2 h1:PAL466mnJw1VolZPm1OarpdUpqukUy/eX4tagia17DM=
github.com/prometheus/prometheus v2.5.0+incompatible h1:7QPitgO2kOFG8ecuRn9O/4L9+10He72rVRJvMXrE9Hg=
github.com/prometheus/prometheus v2.5.0+incompatible/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/taosdata/TDengine v0.0.0-20200511045913-fead5b90383d h1:er7tnj+w7vOo1mLnOL6imwYJdKsvuR2d6/GS8Dyt6oc=
github.com/taosdata/driver-go v0.0.0-20200311072652-8c58c512b6ac h1:uZplMwObJj8mfgI4ZvYPNHRn+fNz2leiMPqShsjtEEc=
github.com/taosdata/driver-go v0.0.0-20200311072652-8c58c512b6ac/go.mod h1:TuMZDpnBrjNO07rneM2C5qMYFqIro4aupL2cUOGGo/I=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0 h1:2mqDk8w/o6UmeUCu5Qiq2y7iMf6anbx+YA8d1JFoFrs=
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20200507105951-43844f6eee31 h1:Bz1qTn2YRWV+9OKJtxHJiQKCiXIdf+kwuKXdt9cBxyU=
google.golang.org/genproto v0.0.0-20200507105951-43844f6eee31/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
cmake_minimum_required (VERSION 2.8.11)
project (dirent LANGUAGES C CXX)
# Initialize C and C++ compilers
enable_language (C CXX)
# Compile in debug mode by default
if (NOT CMAKE_BUILD_TYPE)
set (CMAKE_BUILD_TYPE Debug
CACHE STRING
"Type of build: None Debug Release RelWithDebInfo MinSizeRel."
FORCE
)
endif (NOT CMAKE_BUILD_TYPE)
# Use the include directory only on Windows
if (WIN32)
include_directories (${CMAKE_SOURCE_DIR}/include)
endif (WIN32)
# Install dirent.h
if (WIN32)
install (FILES include/dirent.h DESTINATION include)
endif (WIN32)
# Add distclean target
add_custom_target (distclean
COMMAND ${CMAKE_BUILD_TOOL} clean
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/distclean.cmake
)
# Build example programs
add_executable (find examples/find.c)
add_executable (ls examples/ls.c)
add_executable (locate examples/locate.c)
add_executable (updatedb examples/updatedb.c)
add_executable (scandir examples/scandir.c)
add_executable (cat examples/cat.c)
# Build test programs
include (CTest)
add_custom_target (check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C ${CMAKE_CFG_INTDIR})
function (add_test_executable TEST_NAME)
add_executable (${TEST_NAME} EXCLUDE_FROM_ALL ${ARGN})
add_test (NAME ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND $<TARGET_FILE:${TEST_NAME}>)
add_dependencies (check ${TEST_NAME})
endfunction (add_test_executable)
add_test_executable (t-compile tests/t-compile.c)
add_test_executable (t-dirent tests/t-dirent.c)
add_test_executable (t-scandir tests/t-scandir.c)
add_test_executable (t-unicode tests/t-unicode.c)
add_test_executable (t-cplusplus tests/t-cplusplus.cpp)
2018-05-08 Toni Rönkkö
* Version 1.23.2: fixes bad scandir prototype.
2017-08-27 Toni Rönkkö
* Version 1.23: support readdir_r and scandir functions.
2017-07-18 Toni Rönkkö
* Created release branches v1.22 and v1.21 to Git. Published version
1.22 at softagalleria.net.
2016-09-11 Toni Rönkkö
* Version 1.22: added support for CMake. Thanks to Paul Fultz II.
2014-09-25 Toni Rönkkö
* Version 1.21: compiles correctly under Open Watcom. Thanks to
Virgil Banowetz for a patch!
2014-04-07 Toni Rönkkö
* Version 1.20.1: the zip file from the previous version did not open
correctly with Microsoft's compressed folders. Thanks to Alexandre
for info!
2014-03-17 Toni Ronkko
* Version 1.20: dirent.h compiles correctly in 64-bit architecture.
Thanks to Aaron Simmons!
2014-03-03 Toni Ronkko
* Version 1.13.2: define DT_LNK for compatibility with Unix
programs. Thanks to Joel Bruick for suggestion!
2013-01-27 Toni Ronkko
* Version 1.13.1: patch from Edward Berner fixes set_errno() on
Windows NT 4.0.
* Revised wcstombs() and mbstowcs() wrappers to make sure that they do
not write past their target string.
* PATH_MAX from windows.h includes zero terminator so there is no
need to add one extra byte to variables and structures.
2012-12-12 Toni Ronkko
* Version 1.13: use the traditional 8+3 file naming scheme if a file
name cannot be represented in the default ANSI code page. Now
compiles again with MSVC 6.0. Thanks to Konstantin Khomoutov for
testing.
2012-10-01 Toni Ronkko
* Version 1.12.1: renamed wide-character DIR structure _wDIR to
_WDIR (with capital W) in order to maintain compatibility with MingW.
2012-09-30 Toni Ronkko
* Version 1.12: define PATH_MAX and NAME_MAX. Added wide-character
variants _wDIR, _wdirent, _wopendir(), _wreaddir(), _wclosedir() and
_wrewinddir(). Thanks to Edgar Buerkle and Jan Nijtmans for ideas
and code.
* Now avoiding windows.h. This allows dirent.h to be integrated
more easily into programs using winsock. Thanks to Fernando
Azaldegui.
2011-03-15 Toni Ronkko
* Version 1.11: defined FILE_ATTRIBUTE_DEVICE for MSVC 6.0.
2010-08-11 Toni Ronkko
* Version 1.10: added d_type and d_namlen fields to dirent structure.
The former is especially useful for determining whether directory
entry represents a file or a directory. For more information, see
http://www.delorie.com/gnu/docs/glibc/libc_270.html
* Improved conformance to the standards. For example, errno is now
set properly on failure and assert() is never used. Thanks to Peter
Brockam for suggestions.
* Fixed a bug in rewinddir(): when using relative directory names,
change of working directory no longer causes rewinddir() to fail.
2009-12-15 John Cunningham
* Version 1.9: added rewinddir member function
2008-01-18 Toni Ronkko
* Version 1.8: Using FindFirstFileA and WIN32_FIND_DATAA to avoid
converting string between multi-byte and unicode representations.
This makes the code simpler and also allows the code to be compiled
under MingW. Thanks to Azriel Fasten for the suggestion.
2007-03-04 Toni Ronkko
* Bug fix: due to the strncpy_s() function this file only compiled in
Visual Studio 2005. Using the new string functions only when the
compiler version allows.
2006-11-02 Toni Ronkko
* Major update: removed support for Watcom C, MS-DOS and Turbo C to
simplify the file, updated the code to compile cleanly on Visual
Studio 2005 with both unicode and multi-byte character strings,
removed rewinddir() as it had a bug.
2006-08-20 Toni Ronkko
* Removed all remarks about MSVC 1.0, which is antiqued now.
Simplified comments by removing SGML tags.
2002-05-14 Toni Ronkko
* Embedded the function definitions directly to the header so that no
source modules need to be included in the Visual Studio project.
Removed all the dependencies to other projects so that this header
file can be used independently.
1998-05-28 Toni Ronkko
* First version.
The MIT License (MIT)
Copyright (c) 1998-2019 Toni Ronkko
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.
# Dirent
Dirent is a C/C++ programming interface that allows programmers to retrieve
information about files and directories under Linux/UNIX. This project
provides Linux compatible Dirent interface for Microsoft Windows.
# Installation
Download the latest Dirent installation package from
[GitHub](https://github.com/tronkko/dirent/releases) and
unpack the installation file with 7-zip, for example. The installation
package contains dirent.h file as well as a few example programs and
tests.
## Install Dirent for All Programs
To make dirent.h available for all C/C++ programs, simply copy the
``include/dirent.h`` file to the system include directory. System include
directory contains header files such as assert.h and windows.h. In Visual
Studio 2008, for example, the system include may be found at
``C:\Program Files\Microsoft Visual Studio 9.0\VC\include``.
Everything you need is included in the single dirent.h file, and you can
start using Dirent immediately -- there is no need to add files to your
Visual Studio project.
## Embed Dirent into Your Own Project
If you wish to distribute dirent.h alongside with your own source code, then
copy ``include/dirent.h`` file to a new sub-directory within your project and
add that directory to include path on Windows while omitting the directory
under Linux/UNIX. This allows your project to be compiled against native
dirent.h on Linux/UNIX while substituting the functionality on Microsoft
Windows.
## Examples
The installation package contains four example programs:
Program | Purpose
-------- | -----------------------------------------------------------------
ls | List files in a directory, e.g. ls "c:\Program Files"
find | Find files in subdirectories, e.g. find "c:\Program Files\CMake"
updatedb | Build database of files in a drive, e.g. updatedb c:\
locate | Locate a file from database, e.g. locate notepad.exe
To build the example programs, first install [CMake](https://cmake.org/).
Then, with CMake installed, open command prompt and create a temporary
directory ``c:\temp\dirent`` for the build files as
```
c:\
mkdir temp
mkdir temp\dirent
cd temp\dirent
```
Generate build files as
```
cmake d:\dirent
```
where ``d:\dirent`` is the root directory of the Dirent package (containing
this README.md and LICENSE file).
Once CMake is finished, open Visual Studio, load the generated dirent.sln file
from the build directory and build the solution. Once the build completes, run
the example programs from the command prompt as
```
cd Debug
ls .
find .
updatedb c:\
locate cmd.exe
```
# Copying
Dirent may be freely distributed under the MIT license. See the
[LICENSE](LICENSE) file for details.
# Alternatives to Dirent
I ported Dirent to Microsoft Windows in 1998 when only a few alternatives
were available. However, the situation has changed since then and nowadays
both [Cygwin](http://www.cygwin.com) and [MingW](http://www.mingw.org)
allow you to compile a great number of UNIX programs in Microsoft Windows.
They both provide a full dirent API as well as many other UNIX APIs. MingW
can even be used for commercial applications!
# Remove CMake generated temporary files
set (cmake_generated
${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj
${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj.filters
${CMAKE_BINARY_DIR}/CMakeCache.txt
${CMAKE_BINARY_DIR}/CMakeFiles
${CMAKE_BINARY_DIR}/CTestTestfile.cmake
${CMAKE_BINARY_DIR}/Continuous.vcxproj
${CMAKE_BINARY_DIR}/Continuous.vcxproj.filters
${CMAKE_BINARY_DIR}/DartConfiguration.tcl
${CMAKE_BINARY_DIR}/Debug
${CMAKE_BINARY_DIR}/Experimental.vcxproj
${CMAKE_BINARY_DIR}/Experimental.vcxproj.filters
${CMAKE_BINARY_DIR}/INSTALL.vcxproj
${CMAKE_BINARY_DIR}/INSTALL.vcxproj.filters
${CMAKE_BINARY_DIR}/Makefile
${CMAKE_BINARY_DIR}/Nightly.vcxproj
${CMAKE_BINARY_DIR}/Nightly.vcxproj.filters
${CMAKE_BINARY_DIR}/NightlyMemoryCheck.vcxproj
${CMAKE_BINARY_DIR}/NightlyMemoryCheck.vcxproj.filters
${CMAKE_BINARY_DIR}/RUN_TESTS.vcxproj
${CMAKE_BINARY_DIR}/RUN_TESTS.vcxproj.filters
${CMAKE_BINARY_DIR}/Testing
${CMAKE_BINARY_DIR}/Win32
${CMAKE_BINARY_DIR}/ZERO_CHECK.vcxproj
${CMAKE_BINARY_DIR}/ZERO_CHECK.vcxproj.filters
${CMAKE_BINARY_DIR}/check.vcxproj
${CMAKE_BINARY_DIR}/check.vcxproj.filters
${CMAKE_BINARY_DIR}/cmake_install.cmake
${CMAKE_BINARY_DIR}/dirent.sln
${CMAKE_BINARY_DIR}/distclean.vcxproj
${CMAKE_BINARY_DIR}/distclean.vcxproj.filters
${CMAKE_BINARY_DIR}/find
${CMAKE_BINARY_DIR}/find.dir
${CMAKE_BINARY_DIR}/find.vcxproj
${CMAKE_BINARY_DIR}/find.vcxproj.filters
${CMAKE_BINARY_DIR}/locate
${CMAKE_BINARY_DIR}/locate.dir
${CMAKE_BINARY_DIR}/locate.vcxproj
${CMAKE_BINARY_DIR}/locate.vcxproj.filters
${CMAKE_BINARY_DIR}/ls
${CMAKE_BINARY_DIR}/ls.dir
${CMAKE_BINARY_DIR}/ls.vcxproj
${CMAKE_BINARY_DIR}/ls.vcxproj.filters
${CMAKE_BINARY_DIR}/t-compile
${CMAKE_BINARY_DIR}/t-compile.dir
${CMAKE_BINARY_DIR}/t-compile.vcxproj
${CMAKE_BINARY_DIR}/t-compile.vcxproj.filters
${CMAKE_BINARY_DIR}/t-dirent
${CMAKE_BINARY_DIR}/t-dirent.dir
${CMAKE_BINARY_DIR}/t-dirent.vcxproj
${CMAKE_BINARY_DIR}/t-dirent.vcxproj.filters
${CMAKE_BINARY_DIR}/updatedb
${CMAKE_BINARY_DIR}/updatedb.dir
${CMAKE_BINARY_DIR}/updatedb.vcxproj
${CMAKE_BINARY_DIR}/updatedb.vcxproj.filters
)
foreach (file ${cmake_generated})
if (EXISTS ${file})
file (REMOVE_RECURSE ${file})
endif()
endforeach (file)
/*
* Output contents of a file.
*
* Compile this file with Visual Studio and run the produced command in
* console with a file name argument. For example, command
*
* cat include\dirent.h
*
* will output the dirent.h to screen.
*
* Copyright (C) 1998-2019 Toni Ronkko
* This file is part of dirent. Dirent may be freely distributed
* under the MIT license. For all details and documentation, see
* https://github.com/tronkko/dirent
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <locale.h>
static void output_file (const char *fn);
int
main(
int argc, char *argv[])
{
int i;
/* Select default locale */
setlocale (LC_ALL, "");
/* Require at least one file */
if (argc == 1) {
fprintf (stderr, "Usage: cat filename\n");
return EXIT_FAILURE;
}
/* For each file name argument in command line */
i = 1;
while (i < argc) {
output_file (argv[i]);
i++;
}
return EXIT_SUCCESS;
}
/*
* Output file to screen
*/
static void
output_file(
const char *fn)
{
FILE *fp;
/* Open file */
fp = fopen (fn, "r");
if (fp != NULL) {
size_t n;
char buffer[4096];
/* Output file to screen */
do {
/* Read some bytes from file */
n = fread (buffer, 1, 4096, fp);
/* Output bytes to screen */
fwrite (buffer, 1, n, stdout);
} while (n != 0);
/* Close file */
fclose (fp);
} else {
/* Could not open directory */
fprintf (stderr, "Cannot open %s (%s)\n", fn, strerror (errno));
exit (EXIT_FAILURE);
}
}
/*
* An example demonstrating recursive directory traversal.
*
* Compile this file with Visual Studio and run the produced command in
* console with a directory name argument. For example, command
*
* find "C:\Program Files"
*
* will output thousands of file names such as
*
* c:\Program Files/7-Zip/7-zip.chm
* c:\Program Files/7-Zip/7-zip.dll
* c:\Program Files/7-Zip/7z.dll
* c:\Program Files/Adobe/Reader 10.0/Reader/logsession.dll
* c:\Program Files/Adobe/Reader 10.0/Reader/LogTransport2.exe
* c:\Program Files/Windows NT/Accessories/wordpad.exe
* c:\Program Files/Windows NT/Accessories/write.wpc
*
* The find command provided by this file is only an example: the command does
* not provide options to restrict the output to certain files as the Linux
* version does.
*
* Copyright (C) 1998-2019 Toni Ronkko
* This file is part of dirent. Dirent may be freely distributed
* under the MIT license. For all details and documentation, see
* https://github.com/tronkko/dirent
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <locale.h>
static int find_directory (const char *dirname);
int
main(
int argc, char *argv[])
{
int i;
int ok;
/* Select default locale */
setlocale (LC_ALL, "");
/* For each directory in command line */
i = 1;
while (i < argc) {
ok = find_directory (argv[i]);
if (!ok) {
exit (EXIT_FAILURE);
}
i++;
}
/* List current working directory if no arguments on command line */
if (argc == 1) {
find_directory (".");
}
return EXIT_SUCCESS;
}
/* Find files and subdirectories recursively */
static int
find_directory(
const char *dirname)
{
DIR *dir;
char buffer[PATH_MAX + 2];
char *p = buffer;
const char *src;
char *end = &buffer[PATH_MAX];
int ok;
/* Copy directory name to buffer */
src = dirname;
while (p < end && *src != '\0') {
*p++ = *src++;
}
*p = '\0';
/* Open directory stream */
dir = opendir (dirname);
if (dir != NULL) {
struct dirent *ent;
/* Print all files and directories within the directory */
while ((ent = readdir (dir)) != NULL) {
char *q = p;
char c;
/* Get final character of directory name */
if (buffer < q) {
c = q[-1];
} else {
c = ':';
}
/* Append directory separator if not already there */
if (c != ':' && c != '/' && c != '\\') {
*q++ = '/';
}
/* Append file name */
src = ent->d_name;
while (q < end && *src != '\0') {
*q++ = *src++;
}
*q = '\0';
/* Decide what to do with the directory entry */
switch (ent->d_type) {
case DT_LNK:
case DT_REG:
/* Output file name with directory */
printf ("%s\n", buffer);
break;
case DT_DIR:
/* Scan sub-directory recursively */
if (strcmp (ent->d_name, ".") != 0
&& strcmp (ent->d_name, "..") != 0) {
find_directory (buffer);
}
break;
default:
/* Ignore device entries */
/*NOP*/;
}
}
closedir (dir);
ok = 1;
} else {
/* Could not open directory */
fprintf (stderr, "Cannot open %s (%s)\n", dirname, strerror (errno));
ok = 0;
}
return ok;
}
/*
* A file look-up utility to complement updatedb
*
* Compile and run updatedb command first to create locate.db file. Then,
* compile this program with Visual Studio and run the program in console with
* a file name argument. For example, the command
*
* locate autoexec
*
* might output something like
*
* c:/AUTOEXEC.BAT
* c:/WINDOWS/repair/autoexec.nt
* c:/WINDOWS/system32/AUTOEXEC.NT
*
* Be ware that this file uses wide-character API which is not compatible
* with Linux or other major Unixes.
*
* Copyright (C) 1998-2019 Toni Ronkko
* This file is part of dirent. Dirent may be freely distributed
* under the MIT license. For all details and documentation, see
* https://github.com/tronkko/dirent
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#ifdef WIN32
# include <io.h>
# include <fcntl.h>
#endif
#include <dirent.h>
/* File name and location of database file */
#define DB_LOCATION L"locate.db"
/* Forward-decl */
static int db_locate (const wchar_t *pattern);
static int db_match (const wchar_t *fn, const wchar_t *pattern);
static void db_open (void);
static void db_close (void);
static int db_read (wchar_t *buffer, size_t max);
/* Module local variables */
static FILE *db = NULL;
int
main(
int argc, char *argv[])
{
#ifdef WIN32
int i;
/* Prepare for unicode output */
_setmode (_fileno (stdout), _O_U16TEXT);
/* For each pattern in command line */
i = 1;
while (i < argc) {
wchar_t buffer[PATH_MAX + 1];
errno_t error;
size_t n;
int count = 0;
/* Convert ith argument to wide-character string */
error = mbstowcs_s (&n, buffer, PATH_MAX, argv[i], _TRUNCATE);
if (!error) {
/* Find files matching pattern */
count = db_locate (buffer);
/* Output warning if string is not found */
if (count == 0) {
wprintf (L"%s not found\n", buffer);
}
}
i++;
}
if (argc < 2) {
wprintf (L"Usage: locate pattern\n");
exit (EXIT_FAILURE);
}
#else
printf ("locate only works on Microsoft Windows\n");
#endif
return EXIT_SUCCESS;
}
/* Match pattern against files in locate.db file */
static int
db_locate(
const wchar_t *pattern)
{
int count = 0;
#ifdef WIN32
wchar_t buffer[PATH_MAX + 1];
/* Open locate.db for read */
db_open ();
/* Read one directory and file name at a time from database file */
while (db_read (buffer, PATH_MAX)) {
/* See if file name in buffer matches the search pattern */
if (db_match (buffer, pattern)) {
/* Match found => output file name and path */
wprintf (L"%s\n", buffer);
count++;
}
}
db_close ();
#endif
return count;
}
/* Match pattern against file name */
static int
db_match(
const wchar_t *fn, const wchar_t *pattern)
{
int found = 0;
#ifdef WIN32
wchar_t *p;
wchar_t base[PATH_MAX + 1];
wchar_t patt[PATH_MAX + 1];
int i;
int done = 0;
/* Locate zero-terminator from fn */
p = wcschr (fn, '\0');
/* Find base name from buffer */
while (fn < p && !done) {
switch (p[-1]) {
case ':':
case '/':
case '\\':
/* Final path separator found */
done = 1;
break;
default:
/* No path separator yet */
p--;
}
}
/* Convert base name to lower case */
i = 0;
while (i < PATH_MAX && p[i] != '\0') {
base[i] = towlower (p[i]);
i++;
}
base[i] = '\0';
/* Convert search pattern to lower case */
i = 0;
while (i < PATH_MAX && pattern[i] != '\0') {
patt[i] = towlower (pattern[i]);
i++;
}
patt[i] = '\0';
/* See if file name matches pattern */
if (wcsstr (base, patt) != NULL) {
found = 1;
} else {
found = 0;
}
#endif
return found;
}
/*
* Read line from locate.db. This function is same as fgetws() except
* that new-line at the end of line is not included.
*/
static int
db_read(
wchar_t *buffer, size_t max)
{
int ok = 0;
#ifdef WIN32
size_t i = 0;
wchar_t c;
int done = 0;
do {
/* Read wide-character from stream */
if (db) {
c = fgetwc (db);
} else {
wprintf (L"Database not open\n");
exit (EXIT_SUCCESS);
}
/* Determine how to process character */
switch (c) {
case '\r':
/* Ignore, should be handled by run-time libraries */
/*NOP*/;
break;
case '\n':
/* End of string => return file name and true */
done = 1;
ok = 1;
break;
case /*EOF*/WEOF:
/* End of file */
done = 1;
if (i == 0) {
/* No data in buffer => return false to indicate EOF */
ok = 0;
} else {
/* Data in buffer => return true */
ok = 1;
}
break;
default:
/* Store character */
if (i < max - 1) {
buffer[i++] = c;
} else {
wprintf (L"Buffer too small");
exit (EXIT_FAILURE);
}
}
} while (!done);
/* Zero-terminate buffer */
buffer[i] = '\0';
#endif
return ok;
}
/* Open database file locate.db */
static void
db_open(
void)
{
#ifdef WIN32
if (db == NULL) {
errno_t error;
/* Open file for writing */
error = _wfopen_s (&db, DB_LOCATION, L"rt, ccs=UNICODE");
if (error) {
wprintf (L"Cannot open %s\n", DB_LOCATION);
exit (EXIT_FAILURE);
}
}
#endif
}
/* Close database file */
static void
db_close(
void)
{
if (db) {
fclose (db);
db = NULL;
}
}
/*
* An example demonstrating basic directory listing.
*
* Compile this file with Visual Studio and run the produced command in
* console with a directory name argument. For example, command
*
* ls "c:\Program Files"
*
* might output something like
*
* ./
* ../
* 7-Zip/
* Internet Explorer/
* Microsoft Visual Studio 9.0/
* Microsoft.NET/
* Mozilla Firefox/
*
* The ls command provided by this file is only an example: the command does
* not have any fancy options like "ls -al" in Linux and the command does not
* support file name matching like "ls *.c".
*
* Copyright (C) 1998-2019 Toni Ronkko
* This file is part of dirent. Dirent may be freely distributed
* under the MIT license. For all details and documentation, see
* https://github.com/tronkko/dirent
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <locale.h>
static void list_directory (const char *dirname);
int
main(
int argc, char *argv[])
{
int i;
/* Select default locale */
setlocale (LC_ALL, "");
/* For each directory in command line */
i = 1;
while (i < argc) {
list_directory (argv[i]);
i++;
}
/* List current working directory if no arguments on command line */
if (argc == 1) {
list_directory (".");
}
return EXIT_SUCCESS;
}
/*
* List files and directories within a directory.
*/
static void
list_directory(
const char *dirname)
{
DIR *dir;
struct dirent *ent;
/* Open directory stream */
dir = opendir (dirname);
if (dir != NULL) {
/* Print all files and directories within the directory */
while ((ent = readdir (dir)) != NULL) {
switch (ent->d_type) {
case DT_REG:
printf ("%s\n", ent->d_name);
break;
case DT_DIR:
printf ("%s/\n", ent->d_name);
break;
case DT_LNK:
printf ("%s@\n", ent->d_name);
break;
default:
printf ("%s*\n", ent->d_name);
}
}
closedir (dir);
} else {
/* Could not open directory */
fprintf (stderr, "Cannot open %s (%s)\n", dirname, strerror (errno));
exit (EXIT_FAILURE);
}
}
/*
* Example program demonstrating the use of scandir function.
*
* Compile this file with Visual Studio and run the produced command in
* console with a directory name argument. For example, command
*
* scandir "c:\Program Files"
*
* might output something like
*
* ./
* ../
* 7-Zip/
* Internet Explorer/
* Microsoft Visual Studio 9.0/
* Microsoft.NET/
* Mozilla Firefox/
*
* Copyright (C) 1998-2019 Toni Ronkko
* This file is part of dirent. Dirent may be freely distributed
* under the MIT license. For all details and documentation, see
* https://github.com/tronkko/dirent
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <locale.h>
static void list_directory (const char *dirname);
int
main(
int argc, char *argv[])
{
int i;
/* Select default locale */
setlocale (LC_ALL, "");
/* For each directory in command line */
i = 1;
while (i < argc) {
list_directory (argv[i]);
i++;
}
/* List current working directory if no arguments on command line */
if (argc == 1) {
list_directory (".");
}
return EXIT_SUCCESS;
}
/*
* List files and directories within a directory.
*/
static void
list_directory(
const char *dirname)
{
struct dirent **files;
int i;
int n;
/* Scan files in directory */
n = scandir (dirname, &files, NULL, alphasort);
if (n >= 0) {
/* Loop through file names */
for (i = 0; i < n; i++) {
struct dirent *ent;
/* Get pointer to file entry */
ent = files[i];
/* Output file name */
switch (ent->d_type) {
case DT_REG:
printf ("%s\n", ent->d_name);
break;
case DT_DIR:
printf ("%s/\n", ent->d_name);
break;
case DT_LNK:
printf ("%s@\n", ent->d_name);
break;
default:
printf ("%s*\n", ent->d_name);
}
}
/* Release file names */
for (i = 0; i < n; i++) {
free (files[i]);
}
free (files);
} else {
fprintf (stderr, "Cannot open %s (%s)\n", dirname, strerror (errno));
exit (EXIT_FAILURE);
}
}
/*
* An example demonstrating wide-character functions
*
* Compile this file with Visual Studio and run the produced command in
* console with a directory name argument. For example, command
*
* updatedb C:\
*
* will produce the file locate.db with one file name per line such as
*
* c:\Program Files/7-Zip/7-zip.chm
* c:\Program Files/7-Zip/7-zip.dll
* c:\Program Files/7-Zip/7z.dll
* c:\Program Files/Adobe/Reader 10.0/Reader/logsession.dll
* c:\Program Files/Adobe/Reader 10.0/Reader/LogTransport2.exe
* c:\Program Files/Windows NT/Accessories/wordpad.exe
* c:\Program Files/Windows NT/Accessories/write.wpc
*
* Be ware that this file uses wide-character API which is not compatible
* with Linux or other major Unixes.
*
* Copyright (C) 1998-2019 Toni Ronkko
* This file is part of dirent. Dirent may be freely distributed
* under the MIT license. For all details and documentation, see
* https://github.com/tronkko/dirent
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#ifdef WIN32
# include <io.h>
# include <fcntl.h>
#endif
#include <dirent.h>
/* File name and location of database file */
#define DB_LOCATION L"locate.db"
/* Forward-decl */
static int update_directory (const wchar_t *dirname);
static void db_open (void);
static void db_close (void);
static void db_store (const wchar_t *dirname);
/* Module local variables */
static FILE *db = NULL;
int
main(
int argc, char *argv[])
{
#ifdef WIN32
int i;
int ok;
/* Prepare for unicode output */
_setmode (_fileno (stdout), _O_U16TEXT);
/* Open locate.db */
db_open ();
/* For each directory in command line */
i = 1;
while (i < argc) {
wchar_t buffer[PATH_MAX + 1];
errno_t error;
size_t n;
/* Convert ith argument to wide-character string */
error = mbstowcs_s (&n, buffer, PATH_MAX, argv[i], _TRUNCATE);
if (!error) {
/* Scan directory for files */
ok = update_directory (buffer);
if (!ok) {
wprintf (L"Cannot open directory %s\n", buffer);
exit (EXIT_FAILURE);
}
}
i++;
}
/* Use current working directory if no arguments on command line */
if (argc == 1) {
update_directory (L".");
}
db_close ();
#else
printf ("updatedb only works on Microsoft Windows\n");
#endif
return EXIT_SUCCESS;
}
/* Find files recursively */
static int
update_directory(
const wchar_t *dirname)
{
int ok = 0;
#ifdef WIN32
_WDIR *dir;
wchar_t buffer[PATH_MAX + 2];
wchar_t *p = buffer;
const wchar_t *src;
wchar_t *end = &buffer[PATH_MAX];
/* Copy directory name to buffer */
src = dirname;
while (p < end && *src != '\0') {
*p++ = *src++;
}
*p = '\0';
/* Open directory stream */
dir = _wopendir (dirname);
if (dir != NULL) {
struct _wdirent *ent;
/* Print all files and directories within the directory */
while ((ent = _wreaddir (dir)) != NULL) {
wchar_t *q = p;
wchar_t c;
/* Get final character of directory name */
if (buffer < q) {
c = q[-1];
} else {
c = ':';
}
/* Append directory separator if not already there */
if (c != ':' && c != '/' && c != '\\') {
*q++ = '/';
}
/* Append file name */
src = ent->d_name;
while (q < end && *src != '\0') {
*q++ = *src++;
}
*q = '\0';
/* Decide what to do with the directory entry */
switch (ent->d_type) {
case DT_REG:
/* Store file name */
db_store (buffer);
break;
case DT_DIR:
/* Scan sub-directory recursively */
if (wcscmp (ent->d_name, L".") != 0
&& wcscmp (ent->d_name, L"..") != 0) {
update_directory (buffer);
}
break;
default:
/* Do not device entries */
/*NOP*/;
}
}
wclosedir (dir);
ok = 1;
} else {
/* Cannot open directory */
ok = 0;
}
#endif
return ok;
}
/* Store file name to locate.db */
static void
db_store(
const wchar_t *dirname)
{
#ifdef WIN32
if (db) {
/* Output line to file */
fwprintf (db, L"%s\n", dirname);
} else {
wprintf (L"Database not open\n");
exit (EXIT_FAILURE);
}
#endif
}
/* Open database file locate.db */
static void
db_open(
void)
{
#ifdef WIN32
if (db == NULL) {
errno_t error;
/* Open file for writing */
error = _wfopen_s (&db, DB_LOCATION, L"wt, ccs=UNICODE");
if (error) {
wprintf (L"Cannot open %s\n", DB_LOCATION);
exit (EXIT_FAILURE);
}
}
#endif
}
/* Close database file */
static void
db_close(
void)
{
if (db) {
fclose (db);
db = NULL;
}
}
此差异已折叠。
This file ensures that the directory dir will be created accordingly when
you unzip dirent to your computer. The directory itself is needed by the
test program t-dirent.
This dummy file is needed by the test program t-dirent.
This directory contains some random files for the t-scandir test program. The
files are empty and only the file names matter.
/*
* Test program to make sure that dirent compiles cleanly with winsock.
*
* Copyright (C) 1998-2019 Toni Ronkko
* This file is part of dirent. Dirent may be freely distributed
* under the MIT license. For all details and documentation, see
* https://github.com/tronkko/dirent
*/
#include <dirent.h>
#ifdef WIN32
# include <winsock2.h>
# include <ws2tcpip.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(
int argc, char *argv[])
{
struct dirent *dirp = NULL;
(void) argc;
(void) argv;
#ifdef _DIRENT_HAVE_D_TYPE
printf ("Has d_type\n");
#endif
#ifdef _DIRENT_HAVE_D_NAMLEN
printf ("Has d_namlen\n");
#endif
#ifdef _D_EXACT_NAMLEN
printf ("Has _D_EXACT_NAMLEN\n");
#endif
#ifdef _D_ALLOC_NAMLEN
printf ("Has _D_ALLOC_NAMLEN\n");
#endif
#ifdef _D_ALLOC_NAMLEN
printf ("Has _D_ALLOC_NAMLEN\n");
#endif
printf ("Length of d_name with terminator: %d\n",
(int) sizeof (dirp->d_name));
printf ("OK\n");
return EXIT_SUCCESS;
}
/*
* Test program to make sure that dirent compiles cleanly with C++
*
* Copyright (C) 1998-2019 Toni Ronkko
* This file is part of dirent. Dirent may be freely distributed
* under the MIT license. For all details and documentation, see
* https://github.com/tronkko/dirent
*/
#include <iostream>
#include <string.h>
#include <dirent.h>
#include <assert.h>
using namespace std;
/* Filter and sort functions */
static int only_readme (const struct dirent *entry);
int
main(
int argc, char *argv[])
{
(void) argc;
(void) argv;
/* Basic directory retrieval */
{
DIR *dir;
struct dirent *ent;
int found = 0;
/* Open directory */
dir = opendir ("tests/1");
if (dir == NULL) {
cerr << "Directory tests/1 not found" << endl;
abort ();
}
/* Read entries */
while ((ent = readdir (dir)) != NULL) {
/* Check each file */
if (strcmp (ent->d_name, ".") == 0) {
/* Directory itself */
#ifdef _DIRENT_HAVE_D_TYPE
assert (ent->d_type == DT_DIR);
#endif
#ifdef _DIRENT_HAVE_D_NAMLEN
assert (ent->d_namlen == 1);
#endif
#ifdef _D_EXACT_NAMLEN
assert (_D_EXACT_NAMLEN(ent) == 1);
#endif
#ifdef _D_ALLOC_NAMLEN
assert (_D_ALLOC_NAMLEN(ent) > 1);
#endif
found += 1;
} else if (strcmp (ent->d_name, "..") == 0) {
/* Parent directory */
#ifdef _DIRENT_HAVE_D_TYPE
assert (ent->d_type == DT_DIR);
#endif
#ifdef _DIRENT_HAVE_D_NAMLEN
assert (ent->d_namlen == 2);
#endif
#ifdef _D_EXACT_NAMLEN
assert (_D_EXACT_NAMLEN(ent) == 2);
#endif
#ifdef _D_ALLOC_NAMLEN
assert (_D_ALLOC_NAMLEN(ent) > 2);
#endif
found += 2;
} else if (strcmp (ent->d_name, "file") == 0) {
/* Regular file */
#ifdef _DIRENT_HAVE_D_TYPE
assert (ent->d_type == DT_REG);
#endif
#ifdef _DIRENT_HAVE_D_NAMLEN
assert (ent->d_namlen == 4);
#endif
#ifdef _D_EXACT_NAMLEN
assert (_D_EXACT_NAMLEN(ent) == 4);
#endif
#ifdef _D_ALLOC_NAMLEN
assert (_D_ALLOC_NAMLEN(ent) > 4);
#endif
found += 4;
} else if (strcmp (ent->d_name, "dir") == 0) {
/* Just a directory */
#ifdef _DIRENT_HAVE_D_TYPE
assert (ent->d_type == DT_DIR);
#endif
#ifdef _DIRENT_HAVE_D_NAMLEN
assert (ent->d_namlen == 3);
#endif
#ifdef _D_EXACT_NAMLEN
assert (_D_EXACT_NAMLEN(ent) == 3);
#endif
#ifdef _D_ALLOC_NAMLEN
assert (_D_ALLOC_NAMLEN(ent) > 3);
#endif
found += 8;
} else {
/* Other file */
cerr << "Unexpected file " << ent->d_name << endl;
abort ();
}
}
/* Make sure that all files were found */
assert (found == 0xf);
closedir (dir);
}
/* Basic scan with simple filter function */
{
struct dirent **files;
int n;
int i;
/* Read directory entries */
n = scandir ("tests/3", &files, only_readme, alphasort);
assert (n == 1);
/* Make sure that the filter works */
assert (strcmp (files[0]->d_name, "README.txt") == 0);
/* Release file names */
for (i = 0; i < n; i++) {
free (files[i]);
}
free (files);
}
cout << "OK" << endl;
return EXIT_SUCCESS;
}
/* Only pass README.txt file */
static int
only_readme (const struct dirent *entry)
{
int pass;
if (strcmp (entry->d_name, "README.txt") == 0) {
pass = 1;
} else {
pass = 0;
}
return pass;
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(TDengine)
IF (TD_WINDOWS_64)
IF (TD_WINDOWS_64 OR TD_WINDOWS_32)
LIST(APPEND SRC iconv.c)
LIST(APPEND SRC localcharset.c)
INCLUDE_DIRECTORIES(.)
......
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(TDengine)
IF (TD_WINDOWS_64)
IF (TD_WINDOWS_64 OR TD_WINDOWS_32)
INCLUDE_DIRECTORIES(.)
LIST(APPEND SRC pthread.c)
ADD_LIBRARY(pthread ${SRC})
......
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(TDengine)
IF (TD_WINDOWS_64)
IF (TD_WINDOWS_64 OR TD_WINDOWS_32)
INCLUDE_DIRECTORIES(inc .)
LIST(APPEND SRC regex.c)
ADD_LIBRARY(regex ${SRC})
......
......@@ -31,7 +31,7 @@ IF ((TD_LINUX_64) OR (TD_LINUX_32 AND TD_ARM))
MESSAGE(STATUS "build version ${VERSION_INFO}")
SET_TARGET_PROPERTIES(taos PROPERTIES VERSION ${VERSION_INFO} SOVERSION 1)
ELSEIF (TD_WINDOWS_64)
ELSEIF (TD_WINDOWS_64 OR TD_WINDOWS_32)
INCLUDE_DIRECTORIES(${TD_COMMUNITY_DIR}/deps/jni/windows)
INCLUDE_DIRECTORIES(${TD_COMMUNITY_DIR}/deps/jni/windows/win32)
INCLUDE_DIRECTORIES(${TD_COMMUNITY_DIR}/deps/pthread)
......
......@@ -142,8 +142,8 @@ JNIEXPORT jlong JNICALL Java_com_taosdata_jdbc_TSDBJNIConnector_subscribeImp
* Method: consumeImp
* Signature: (J)Lcom/taosdata/jdbc/TSDBResultSetRowData;
*/
JNIEXPORT jobject JNICALL Java_com_taosdata_jdbc_TSDBJNIConnector_consumeImp
(JNIEnv *, jobject, jlong, jint);
JNIEXPORT jlong JNICALL Java_com_taosdata_jdbc_TSDBJNIConnector_consumeImp
(JNIEnv *, jobject, jlong);
/*
* Class: com_taosdata_jdbc_TSDBJNIConnector
......
......@@ -606,7 +606,7 @@ static jobject convert_one_row(JNIEnv *env, TAOS_ROW row, TAOS_FIELD* fields, in
return rowobj;
}
JNIEXPORT jobject JNICALL Java_com_taosdata_jdbc_TSDBJNIConnector_consumeImp(JNIEnv *env, jobject jobj, jlong sub, jint timeout) {
JNIEXPORT jlong JNICALL Java_com_taosdata_jdbc_TSDBJNIConnector_consumeImp(JNIEnv *env, jobject jobj, jlong sub) {
jniTrace("jobj:%p, in TSDBJNIConnector_consumeImp, sub:%ld", jobj, sub);
jniGetGlobalMethod(env);
......@@ -616,38 +616,14 @@ JNIEXPORT jobject JNICALL Java_com_taosdata_jdbc_TSDBJNIConnector_consumeImp(JNI
int64_t start = taosGetTimestampMs();
int count = 0;
while (true) {
TAOS_RES * res = taos_consume(tsub);
if (res == NULL) {
jniError("jobj:%p, tsub:%p, taos_consume returns NULL", jobj, tsub);
return NULL;
}
TAOS_FIELD *fields = taos_fetch_fields(res);
int num_fields = taos_num_fields(res);
while (true) {
TAOS_ROW row = taos_fetch_row(res);
if (row == NULL) {
break;
}
jobject rowobj = convert_one_row(env, row, fields, num_fields);
(*env)->CallBooleanMethod(env, rows, g_arrayListAddFp, rowobj);
count++;
}
TAOS_RES *res = taos_consume(tsub);
if (count > 0) {
break;
}
if (timeout == -1) {
continue;
}
if (((int)(taosGetTimestampMs() - start)) >= timeout) {
jniTrace("jobj:%p, sub:%ld, timeout", jobj, sub);
break;
}
if (res == NULL) {
jniError("jobj:%p, tsub:%p, taos_consume returns NULL", jobj, tsub);
return NULL;
}
return rows;
return res;
}
JNIEXPORT void JNICALL Java_com_taosdata_jdbc_TSDBJNIConnector_unsubscribeImp(JNIEnv *env, jobject jobj, jlong sub, jboolean keepProgress) {
......
此差异已折叠。
......@@ -3669,7 +3669,7 @@ int WCSPatternMatch(const wchar_t *patterStr, const wchar_t *str, size_t size, c
wchar_t accept[3] = {towupper(c), towlower(c), 0};
while (1) {
size_t n = wcsspn(str, accept);
size_t n = wcscspn(str, accept);
str += n;
if (str[0] == 0 || (n >= size - 1)) {
......@@ -3678,7 +3678,7 @@ int WCSPatternMatch(const wchar_t *patterStr, const wchar_t *str, size_t size, c
str++;
int32_t ret = WCSPatternMatch(&patterStr[i], str, wcslen(str), pInfo);
int32_t ret = WCSPatternMatch(&patterStr[i], str, twcslen(str), pInfo);
if (ret != TSDB_PATTERN_NOMATCH) {
return ret;
}
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -244,4 +244,5 @@ char *tsError[] = {"success",
"failed to lock resources",
"table id/uid mismatch",
"client query cache erased", // 119
"too many authentication failed, try 10 minutes later", //120
};
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册