]> git.droids-corp.org - ucgine.git/commitdiff
initial revision master
authorOlivier Matz <zer0@droids-corp.org>
Mon, 28 Sep 2015 17:03:55 +0000 (19:03 +0200)
committerOlivier Matz <zer0@droids-corp.org>
Mon, 4 Apr 2016 21:35:00 +0000 (23:35 +0200)
178 files changed:
arch/avr/include/errno.h [new file with mode: 0644]
arch/avr/include/fcntl.h [new file with mode: 0644]
arch/avr/include/stdio.h [new file with mode: 0644]
arch/avr/include/sys/queue.h [new file with mode: 0644]
arch/avr/include/sys/types.h [new file with mode: 0644]
arch/avr/include/ucg_delay.h [new file with mode: 0644]
arch/avr/include/ucg_irq.h [new file with mode: 0644]
arch/avr/include/ucg_reent_intr.h [new file with mode: 0644]
arch/avr/include/unistd.h [new file with mode: 0644]
arch/avr/mk/ucgine-arch.mk [new file with mode: 0644]
arch/avr/uart/include/ucg_avr_uart.h [new file with mode: 0644]
arch/avr/uart/ucg_avr_uart.c [new file with mode: 0644]
arch/posix/include/ucg_delay.h [new file with mode: 0644]
arch/posix/include/ucg_irq.h [new file with mode: 0644]
arch/posix/mk/ucgine-arch.mk [new file with mode: 0644]
arch/stm32/include/ucg_delay.h [new file with mode: 0644]
arch/stm32/include/ucg_irq.h [new file with mode: 0644]
arch/stm32/include/ucg_reent_intr.h [new file with mode: 0644]
arch/stm32/mk/ucgine-arch.mk [new file with mode: 0644]
arch/stm32/uart/include/ucg_stm32_uart.h [new file with mode: 0644]
arch/stm32/uart/ucg_stm32_uart.c [new file with mode: 0644]
arch/stm32/ucg_reent_intr.c [new file with mode: 0644]
examples/Makefile [new file with mode: 0644]
examples/spi-flash-client/Makefile [new file with mode: 0644]
examples/spi-flash-client/main.c [new file with mode: 0644]
examples/spi-flash-client/uart.c [new file with mode: 0644]
examples/spi-flash-client/uart.h [new file with mode: 0644]
examples/spi-flash/Makefile [new file with mode: 0644]
examples/spi-flash/main.c [new file with mode: 0644]
examples/spi-flash/stm32_flash.ld [new file with mode: 0755]
examples/spi-flash/stm32f4xx_conf.h [new file with mode: 0644]
examples/spi-flash/system_stm32f4xx.c [new file with mode: 0644]
examples/spi-flash/uart.c [new file with mode: 0644]
examples/spi-flash/uart.h [new file with mode: 0644]
examples/test-callout/Makefile [new file with mode: 0644]
examples/test-callout/main.c [new file with mode: 0644]
examples/test-callout/stm32_flash.ld [new file with mode: 0755]
examples/test-callout/stm32f4xx_conf.h [new file with mode: 0644]
examples/test-callout/system_stm32f4xx.c [new file with mode: 0644]
examples/test-cmd/Makefile [new file with mode: 0644]
examples/test-cmd/commands.c [new file with mode: 0644]
examples/test-cmd/commands.h [new file with mode: 0644]
examples/test-cmd/main.c [new file with mode: 0644]
examples/test-cmd/stm32_flash.ld [new file with mode: 0755]
examples/test-cmd/stm32f4xx_conf.h [new file with mode: 0644]
examples/test-cmd/system_stm32f4xx.c [new file with mode: 0644]
examples/test-cmd/uart.c [new file with mode: 0644]
examples/test-cmd/uart.h [new file with mode: 0644]
examples/test-mk/Makefile [new file with mode: 0644]
examples/test-mk/dir/titi.c [new file with mode: 0644]
examples/test-mk/dir/toto.c [new file with mode: 0644]
examples/test-mk/dir/toto.h [new file with mode: 0644]
examples/test-mk/main.c [new file with mode: 0644]
examples/test-mk/test1/Makefile [new file with mode: 0644]
examples/test-mk/test1/main.c [new file with mode: 0644]
examples/test-mk/test2/Makefile [new file with mode: 0644]
examples/test-mk/test2/main.c [new file with mode: 0644]
examples/test-uart/Makefile [new file with mode: 0644]
examples/test-uart/main.c [new file with mode: 0644]
examples/test-uart/stm32_flash.ld [new file with mode: 0755]
examples/test-uart/stm32f4xx_conf.h [new file with mode: 0644]
examples/test-uart/system_stm32f4xx.c [new file with mode: 0644]
examples/test-uart/uart.c [new file with mode: 0644]
examples/test-uart/uart.h [new file with mode: 0644]
lib/callout/include/ucg_callout.h [new file with mode: 0644]
lib/callout/ucg_callout.c [new file with mode: 0644]
lib/cirbuf/include/ucg_cirbuf.h [new file with mode: 0644]
lib/cirbuf/ucg_cirbuf.c [new file with mode: 0644]
lib/cmd/include/ucg_cmd.h [new file with mode: 0644]
lib/cmd/include/ucg_cmd_parse.h [new file with mode: 0644]
lib/cmd/include/ucg_cmd_parse_etheraddr.h [new file with mode: 0644]
lib/cmd/include/ucg_cmd_parse_file.h [new file with mode: 0644]
lib/cmd/include/ucg_cmd_parse_ipaddr.h [new file with mode: 0644]
lib/cmd/include/ucg_cmd_parse_num.h [new file with mode: 0644]
lib/cmd/include/ucg_cmd_parse_string.h [new file with mode: 0644]
lib/cmd/include/ucg_cmd_rdline.h [new file with mode: 0644]
lib/cmd/include/ucg_cmd_socket.h [new file with mode: 0644]
lib/cmd/include/ucg_cmd_termios.h [new file with mode: 0644]
lib/cmd/include/ucg_cmd_vt100.h [new file with mode: 0644]
lib/cmd/ucg_cmd.c [new file with mode: 0644]
lib/cmd/ucg_cmd_parse.c [new file with mode: 0644]
lib/cmd/ucg_cmd_parse_etheraddr.c [new file with mode: 0644]
lib/cmd/ucg_cmd_parse_file.c [new file with mode: 0644]
lib/cmd/ucg_cmd_parse_ipaddr.c [new file with mode: 0644]
lib/cmd/ucg_cmd_parse_num.c [new file with mode: 0644]
lib/cmd/ucg_cmd_parse_string.c [new file with mode: 0644]
lib/cmd/ucg_cmd_rdline.c [new file with mode: 0644]
lib/cmd/ucg_cmd_socket.c [new file with mode: 0644]
lib/cmd/ucg_cmd_termios.c [new file with mode: 0644]
lib/cmd/ucg_cmd_vt100.c [new file with mode: 0644]
lib/gloss/include/ucg_gloss_chardev.h [new file with mode: 0644]
lib/gloss/ucg_gloss_chardev.c [new file with mode: 0644]
lib/gloss/ucg_gloss_stubs.c [new file with mode: 0644]
lib/uart/include/ucg_uart.h [new file with mode: 0644]
lib/uart/ucg_uart.c [new file with mode: 0644]
mk/ucgine-ar-rules.mk [new file with mode: 0644]
mk/ucgine-ar-vars.mk [new file with mode: 0644]
mk/ucgine-clean-rules.mk [new file with mode: 0644]
mk/ucgine-clean-vars.mk [new file with mode: 0644]
mk/ucgine-copy-rules.mk [new file with mode: 0644]
mk/ucgine-copy-vars.mk [new file with mode: 0644]
mk/ucgine-exe-rules.mk [new file with mode: 0644]
mk/ucgine-exe-vars.mk [new file with mode: 0644]
mk/ucgine-obj-rules.mk [new file with mode: 0644]
mk/ucgine-obj-vars.mk [new file with mode: 0644]
mk/ucgine-objcopy-rules.mk [new file with mode: 0644]
mk/ucgine-objcopy-vars.mk [new file with mode: 0644]
mk/ucgine-post.mk [new file with mode: 0644]
mk/ucgine-pre.mk [new file with mode: 0644]
mk/ucgine-shlib-rules.mk [new file with mode: 0644]
mk/ucgine-shlib-vars.mk [new file with mode: 0644]
mk/ucgine-slink-rules.mk [new file with mode: 0644]
mk/ucgine-slink-vars.mk [new file with mode: 0644]
mk/ucgine-subdir-rules.mk [new file with mode: 0644]
mk/ucgine-subdir-vars.mk [new file with mode: 0644]
mk/ucgine-tools.mk [new file with mode: 0644]
mk/ucgine-vars.mk [new file with mode: 0644]
tools/cfzy/Makefile [new file with mode: 0644]
tools/cfzy/build/cfzy-basic/cfzy-basic [new file with mode: 0755]
tools/cfzy/cfzy-basic/Makefile [new file with mode: 0644]
tools/cfzy/cfzy-basic/main.c [new file with mode: 0644]
tools/cfzy/cfzy-test/Makefile [new file with mode: 0644]
tools/cfzy/cfzy-test/invalid-configs/circular-dep.cfzy [new file with mode: 0644]
tools/cfzy/cfzy-test/invalid-configs/circular-dep2.cfzy [new file with mode: 0644]
tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val [new file with mode: 0644]
tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val2 [new file with mode: 0644]
tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val3 [new file with mode: 0644]
tools/cfzy/cfzy-test/invalid-configs/dup-name.cfzy [new file with mode: 0644]
tools/cfzy/cfzy-test/invalid-configs/invalid-attr.cfzy [new file with mode: 0644]
tools/cfzy/cfzy-test/invalid-configs/invalid-command.cfzy [new file with mode: 0644]
tools/cfzy/cfzy-test/invalid-configs/invalid-expr.cfzy [new file with mode: 0644]
tools/cfzy/cfzy-test/invalid-configs/invalid-value.cfzy [new file with mode: 0644]
tools/cfzy/cfzy-test/invalid-configs/node-not-closed.cfzy [new file with mode: 0644]
tools/cfzy/cfzy-test/main.c [new file with mode: 0644]
tools/cfzy/cfzy-test/test-configs/conftree.cfzy [new file with mode: 0644]
tools/cfzy/cfzy-test/test-configs/dotconfig [new file with mode: 0644]
tools/cfzy/cfzy-test/test-configs/subconftree.cfzy [new file with mode: 0644]
tools/cfzy/cfzy-test/test_conftree.c [new file with mode: 0644]
tools/cfzy/cfzy-test/test_conftree.h [new file with mode: 0644]
tools/cfzy/cfzy-test/test_dotconfig.c [new file with mode: 0644]
tools/cfzy/cfzy-test/test_dotconfig.h [new file with mode: 0644]
tools/cfzy/cfzy-test/test_expr.c [new file with mode: 0644]
tools/cfzy/cfzy-test/test_expr.h [new file with mode: 0644]
tools/cfzy/libconfizery/Makefile [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_c_hdr.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_c_hdr.h [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_confnode.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_confnode.h [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_confnode_choice.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_confnode_choiceconfig.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_confnode_comment.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_confnode_config.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_confnode_if.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_confnode_intconfig.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_confnode_menu.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_confnode_menuconfig.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_confnode_ops.h [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_confnode_root.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_confnode_strconfig.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_conftree.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_conftree.h [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_conftree_parser.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_conftree_parser.h [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_dotconfig.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_dotconfig.h [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_expr.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_expr.h [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_expr_graph.py [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_file.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_file.h [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_htable.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_htable.h [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_list.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_list.h [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_log.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_log.h [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_string.c [new file with mode: 0644]
tools/cfzy/libconfizery/cfzy_string.h [new file with mode: 0644]

diff --git a/arch/avr/include/errno.h b/arch/avr/include/errno.h
new file mode 100644 (file)
index 0000000..58b9e75
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)errno.h     8.5 (Berkeley) 1/21/94
+ */
+
+#ifndef UCG_ERRNO_H_
+#define UCG_ERRNO_H_
+
+#define        EPERM           1               /* Operation not permitted */
+#define        ENOENT          2               /* No such file or directory */
+#define        ESRCH           3               /* No such process */
+#define        EINTR           4               /* Interrupted system call */
+#define        EIO             5               /* Input/output error */
+#define        ENXIO           6               /* Device not configured */
+#define        E2BIG           7               /* Argument list too long */
+#define        ENOEXEC         8               /* Exec format error */
+#define        EBADF           9               /* Bad file descriptor */
+#define        ECHILD          10              /* No child processes */
+#define        EDEADLK         11              /* Resource deadlock avoided */
+                                       /* 11 was EAGAIN */
+#define        ENOMEM          12              /* Cannot allocate memory */
+#define        EACCES          13              /* Permission denied */
+#define        EFAULT          14              /* Bad address */
+#define        ENOTBLK         15              /* Block device required */
+#define        EBUSY           16              /* Device busy */
+#define        EEXIST          17              /* File exists */
+#define        EXDEV           18              /* Cross-device link */
+#define        ENODEV          19              /* Operation not supported by device */
+#define        ENOTDIR         20              /* Not a directory */
+#define        EISDIR          21              /* Is a directory */
+#define        EINVAL          22              /* Invalid argument */
+#define        ENFILE          23              /* Too many open files in system */
+#define        EMFILE          24              /* Too many open files */
+#define        ENOTTY          25              /* Inappropriate ioctl for device */
+#define        ETXTBSY         26              /* Text file busy */
+#define        EFBIG           27              /* File too large */
+#define        ENOSPC          28              /* No space left on device */
+#define        ESPIPE          29              /* Illegal seek */
+#define        EROFS           30              /* Read-only file system */
+#define        EMLINK          31              /* Too many links */
+#define        EPIPE           32              /* Broken pipe */
+
+/* math software */
+#define        EDOM            33              /* Numerical argument out of domain */
+#define        ERANGE          34              /* Result too large or too small */
+
+/* non-blocking and interrupt i/o */
+#define        EAGAIN          35              /* Resource temporarily unavailable */
+#define        EWOULDBLOCK     EAGAIN          /* Operation would block */
+#define        EINPROGRESS     36              /* Operation now in progress */
+#define        EALREADY        37              /* Operation already in progress */
+
+/* ipc/network software -- argument errors */
+#define        ENOTSOCK        38              /* Socket operation on non-socket */
+#define        EDESTADDRREQ    39              /* Destination address required */
+#define        EMSGSIZE        40              /* Message too long */
+#define        EPROTOTYPE      41              /* Protocol wrong type for socket */
+#define        ENOPROTOOPT     42              /* Protocol option not available */
+#define        EPROTONOSUPPORT 43              /* Protocol not supported */
+#define        ESOCKTNOSUPPORT 44              /* Socket type not supported */
+#define        EOPNOTSUPP      45              /* Operation not supported */
+#define        EPFNOSUPPORT    46              /* Protocol family not supported */
+#define        EAFNOSUPPORT    47              /* Address family not supported by protocol family */
+#define        EADDRINUSE      48              /* Address already in use */
+#define        EADDRNOTAVAIL   49              /* Can't assign requested address */
+
+/* ipc/network software -- operational errors */
+#define        ENETDOWN        50              /* Network is down */
+#define        ENETUNREACH     51              /* Network is unreachable */
+#define        ENETRESET       52              /* Network dropped connection on reset */
+#define        ECONNABORTED    53              /* Software caused connection abort */
+#define        ECONNRESET      54              /* Connection reset by peer */
+#define        ENOBUFS         55              /* No buffer space available */
+#define        EISCONN         56              /* Socket is already connected */
+#define        ENOTCONN        57              /* Socket is not connected */
+#define        ESHUTDOWN       58              /* Can't send after socket shutdown */
+#define        ETOOMANYREFS    59              /* Too many references: can't splice */
+#define        ETIMEDOUT       60              /* Operation timed out */
+#define        ECONNREFUSED    61              /* Connection refused */
+
+#define        ELOOP           62              /* Too many levels of symbolic links */
+#define        ENAMETOOLONG    63              /* File name too long */
+
+/* should be rearranged */
+#define        EHOSTDOWN       64              /* Host is down */
+#define        EHOSTUNREACH    65              /* No route to host */
+#define        ENOTEMPTY       66              /* Directory not empty */
+
+/* quotas & mush */
+#define        EPROCLIM        67              /* Too many processes */
+#define        EUSERS          68              /* Too many users */
+#define        EDQUOT          69              /* Disc quota exceeded */
+
+/* Network File System */
+#define        ESTALE          70              /* Stale NFS file handle */
+#define        EREMOTE         71              /* Too many levels of remote in path */
+#define        EBADRPC         72              /* RPC struct is bad */
+#define        ERPCMISMATCH    73              /* RPC version wrong */
+#define        EPROGUNAVAIL    74              /* RPC prog. not avail */
+#define        EPROGMISMATCH   75              /* Program version wrong */
+#define        EPROCUNAVAIL    76              /* Bad procedure for program */
+
+#define        ENOLCK          77              /* No locks available */
+#define        ENOSYS          78              /* Function not implemented */
+
+#define        EFTYPE          79              /* Inappropriate file type or format */
+#define        EAUTH           80              /* Authentication error */
+#define        ENEEDAUTH       81              /* Need authenticator */
+
+/* SystemV IPC */
+#define        EIDRM           82              /* Identifier removed */
+#define        ENOMSG          83              /* No message of desired type */
+#define        EOVERFLOW       84              /* Value too large to be stored in data type */
+
+/* Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 */
+#define        EILSEQ          85              /* Illegal byte sequence */
+
+/* From IEEE Std 1003.1-2001 */
+/* Base, Realtime, Threads or Thread Priority Scheduling option errors */
+#define ENOTSUP                86              /* Not supported */
+
+/* Realtime option errors */
+#define ECANCELED      87              /* Operation canceled */
+
+/* Realtime, XSI STREAMS option errors */
+#define EBADMSG                88              /* Bad or Corrupt message */
+
+/* XSI STREAMS option errors  */
+#define ENODATA                89              /* No message available */
+#define ENOSR          90              /* No STREAM resources */
+#define ENOSTR         91              /* Not a STREAM */
+#define ETIME          92              /* STREAM ioctl timeout */
+
+/* File system extended attribute errors */
+#define        ENOATTR         93              /* Attribute not found */
+
+/* Realtime, XSI STREAMS option errors */
+#define        EMULTIHOP       94              /* Multihop attempted */
+#define        ENOLINK         95              /* Link has been severed */
+#define        EPROTO          96              /* Protocol error */
+
+#define        ELAST           96              /* Must equal largest errno */
+
+#include_next <errno.h>
+
+#endif /* !UCG_ERRNO_H_ */
diff --git a/arch/avr/include/fcntl.h b/arch/avr/include/fcntl.h
new file mode 100644 (file)
index 0000000..d949999
--- /dev/null
@@ -0,0 +1 @@
+/* empty, just have it for compat */
diff --git a/arch/avr/include/stdio.h b/arch/avr/include/stdio.h
new file mode 100644 (file)
index 0000000..fd3208c
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_STDIO_H_
+#define UCG_STDIO_H_
+
+#include_next <stdio.h>
+
+static inline void setbuf(FILE *stream, char *buf)
+{
+       /* ignore setbuf, it is not implemented in avr-libc */
+       (void)stream;
+       (void)buf;
+}
+
+#endif /* !UCG_ERRNO_H_ */
diff --git a/arch/avr/include/sys/queue.h b/arch/avr/include/sys/queue.h
new file mode 100644 (file)
index 0000000..00dab04
--- /dev/null
@@ -0,0 +1,844 @@
+/*
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)queue.h     8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef        _SYS_QUEUE_H_
+#define        _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The
+ * elements are singly linked for minimum space and pointer manipulation
+ * overhead at the expense of O(n) removal for arbitrary elements. New
+ * elements can be added to the list after an existing element or at the
+ * head of the list.  Elements being removed from the head of the list
+ * should use the explicit macro for this purpose for optimum
+ * efficiency. A singly-linked list may only be traversed in the forward
+ * direction.  Singly-linked lists are ideal for applications with large
+ * datasets and few or no removals or for implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * Include the definition of NULL only on NetBSD because sys/null.h
+ * is not available elsewhere.  This conditional makes the header
+ * portable and it can simply be dropped verbatim into any system.
+ * The caveat is that on other systems some other header
+ * must provide NULL before the macros can be used.
+ */
+#ifdef __NetBSD__
+#include <sys/null.h>
+#endif
+
+#if defined(QUEUEDEBUG)
+# if defined(_KERNEL)
+#  define QUEUEDEBUG_ABORT(...) panic(__VA_ARGS__)
+# else
+#  include <err.h>
+#  define QUEUEDEBUG_ABORT(...) err(1, __VA_ARGS__)
+# endif
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define        SLIST_HEAD(name, type)                                          \
+struct name {                                                          \
+       struct type *slh_first; /* first element */                     \
+}
+
+#define        SLIST_HEAD_INITIALIZER(head)                                    \
+       { NULL }
+
+#define        SLIST_ENTRY(type)                                               \
+struct {                                                               \
+       struct type *sle_next;  /* next element */                      \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define        SLIST_FIRST(head)       ((head)->slh_first)
+#define        SLIST_END(head)         NULL
+#define        SLIST_EMPTY(head)       ((head)->slh_first == NULL)
+#define        SLIST_NEXT(elm, field)  ((elm)->field.sle_next)
+
+#define        SLIST_FOREACH(var, head, field)                                 \
+       for((var) = (head)->slh_first;                                  \
+           (var) != SLIST_END(head);                                   \
+           (var) = (var)->field.sle_next)
+
+#define        SLIST_FOREACH_SAFE(var, head, field, tvar)                      \
+       for ((var) = SLIST_FIRST((head));                               \
+           (var) != SLIST_END(head) &&                                 \
+           ((tvar) = SLIST_NEXT((var), field), 1);                     \
+           (var) = (tvar))
+
+/*
+ * Singly-linked List functions.
+ */
+#define        SLIST_INIT(head) do {                                           \
+       (head)->slh_first = SLIST_END(head);                            \
+} while (/*CONSTCOND*/0)
+
+#define        SLIST_INSERT_AFTER(slistelm, elm, field) do {                   \
+       (elm)->field.sle_next = (slistelm)->field.sle_next;             \
+       (slistelm)->field.sle_next = (elm);                             \
+} while (/*CONSTCOND*/0)
+
+#define        SLIST_INSERT_HEAD(head, elm, field) do {                        \
+       (elm)->field.sle_next = (head)->slh_first;                      \
+       (head)->slh_first = (elm);                                      \
+} while (/*CONSTCOND*/0)
+
+#define        SLIST_REMOVE_AFTER(slistelm, field) do {                        \
+       (slistelm)->field.sle_next =                                    \
+           SLIST_NEXT(SLIST_NEXT((slistelm), field), field);           \
+} while (/*CONSTCOND*/0)
+
+#define        SLIST_REMOVE_HEAD(head, field) do {                             \
+       (head)->slh_first = (head)->slh_first->field.sle_next;          \
+} while (/*CONSTCOND*/0)
+
+#define        SLIST_REMOVE(head, elm, type, field) do {                       \
+       if ((head)->slh_first == (elm)) {                               \
+               SLIST_REMOVE_HEAD((head), field);                       \
+       }                                                               \
+       else {                                                          \
+               struct type *curelm = (head)->slh_first;                \
+               while(curelm->field.sle_next != (elm))                  \
+                       curelm = curelm->field.sle_next;                \
+               curelm->field.sle_next =                                \
+                   curelm->field.sle_next->field.sle_next;             \
+       }                                                               \
+} while (/*CONSTCOND*/0)
+
+
+/*
+ * List definitions.
+ */
+#define        LIST_HEAD(name, type)                                           \
+struct name {                                                          \
+       struct type *lh_first;  /* first element */                     \
+}
+
+#define        LIST_HEAD_INITIALIZER(head)                                     \
+       { NULL }
+
+#define        LIST_ENTRY(type)                                                \
+struct {                                                               \
+       struct type *le_next;   /* next element */                      \
+       struct type **le_prev;  /* address of previous next element */  \
+}
+
+/*
+ * List access methods.
+ */
+#define        LIST_FIRST(head)                ((head)->lh_first)
+#define        LIST_END(head)                  NULL
+#define        LIST_EMPTY(head)                ((head)->lh_first == LIST_END(head))
+#define        LIST_NEXT(elm, field)           ((elm)->field.le_next)
+
+#define        LIST_FOREACH(var, head, field)                                  \
+       for ((var) = ((head)->lh_first);                                \
+           (var) != LIST_END(head);                                    \
+           (var) = ((var)->field.le_next))
+
+#define        LIST_FOREACH_SAFE(var, head, field, tvar)                       \
+       for ((var) = LIST_FIRST((head));                                \
+           (var) != LIST_END(head) &&                                  \
+           ((tvar) = LIST_NEXT((var), field), 1);                      \
+           (var) = (tvar))
+
+#define        LIST_MOVE(head1, head2) do {                                    \
+       LIST_INIT((head2));                                             \
+       if (!LIST_EMPTY((head1))) {                                     \
+               (head2)->lh_first = (head1)->lh_first;                  \
+               LIST_INIT((head1));                                     \
+       }                                                               \
+} while (/*CONSTCOND*/0)
+
+/*
+ * List functions.
+ */
+#if defined(QUEUEDEBUG)
+#define        QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field)                   \
+       if ((head)->lh_first &&                                         \
+           (head)->lh_first->field.le_prev != &(head)->lh_first)       \
+               QUEUEDEBUG_ABORT("LIST_INSERT_HEAD %p %s:%d", (head),   \
+                   __FILE__, __LINE__);
+#define        QUEUEDEBUG_LIST_OP(elm, field)                                  \
+       if ((elm)->field.le_next &&                                     \
+           (elm)->field.le_next->field.le_prev !=                      \
+           &(elm)->field.le_next)                                      \
+               QUEUEDEBUG_ABORT("LIST_* forw %p %s:%d", (elm),         \
+                   __FILE__, __LINE__);                                \
+       if (*(elm)->field.le_prev != (elm))                             \
+               QUEUEDEBUG_ABORT("LIST_* back %p %s:%d", (elm),         \
+                   __FILE__, __LINE__);
+#define        QUEUEDEBUG_LIST_POSTREMOVE(elm, field)                          \
+       (elm)->field.le_next = (void *)1L;                              \
+       (elm)->field.le_prev = (void *)1L;
+#else
+#define        QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field)
+#define        QUEUEDEBUG_LIST_OP(elm, field)
+#define        QUEUEDEBUG_LIST_POSTREMOVE(elm, field)
+#endif
+
+#define        LIST_INIT(head) do {                                            \
+       (head)->lh_first = LIST_END(head);                              \
+} while (/*CONSTCOND*/0)
+
+#define        LIST_INSERT_AFTER(listelm, elm, field) do {                     \
+       QUEUEDEBUG_LIST_OP((listelm), field)                            \
+       if (((elm)->field.le_next = (listelm)->field.le_next) !=        \
+           LIST_END(head))                                             \
+               (listelm)->field.le_next->field.le_prev =               \
+                   &(elm)->field.le_next;                              \
+       (listelm)->field.le_next = (elm);                               \
+       (elm)->field.le_prev = &(listelm)->field.le_next;               \
+} while (/*CONSTCOND*/0)
+
+#define        LIST_INSERT_BEFORE(listelm, elm, field) do {                    \
+       QUEUEDEBUG_LIST_OP((listelm), field)                            \
+       (elm)->field.le_prev = (listelm)->field.le_prev;                \
+       (elm)->field.le_next = (listelm);                               \
+       *(listelm)->field.le_prev = (elm);                              \
+       (listelm)->field.le_prev = &(elm)->field.le_next;               \
+} while (/*CONSTCOND*/0)
+
+#define        LIST_INSERT_HEAD(head, elm, field) do {                         \
+       QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field)               \
+       if (((elm)->field.le_next = (head)->lh_first) != LIST_END(head))\
+               (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+       (head)->lh_first = (elm);                                       \
+       (elm)->field.le_prev = &(head)->lh_first;                       \
+} while (/*CONSTCOND*/0)
+
+#define        LIST_REMOVE(elm, field) do {                                    \
+       QUEUEDEBUG_LIST_OP((elm), field)                                \
+       if ((elm)->field.le_next != NULL)                               \
+               (elm)->field.le_next->field.le_prev =                   \
+                   (elm)->field.le_prev;                               \
+       *(elm)->field.le_prev = (elm)->field.le_next;                   \
+       QUEUEDEBUG_LIST_POSTREMOVE((elm), field)                        \
+} while (/*CONSTCOND*/0)
+
+#define LIST_REPLACE(elm, elm2, field) do {                            \
+       if (((elm2)->field.le_next = (elm)->field.le_next) != NULL)     \
+               (elm2)->field.le_next->field.le_prev =                  \
+                   &(elm2)->field.le_next;                             \
+       (elm2)->field.le_prev = (elm)->field.le_prev;                   \
+       *(elm2)->field.le_prev = (elm2);                                \
+       QUEUEDEBUG_LIST_POSTREMOVE((elm), field)                        \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Simple queue definitions.
+ */
+#define        SIMPLEQ_HEAD(name, type)                                        \
+struct name {                                                          \
+       struct type *sqh_first; /* first element */                     \
+       struct type **sqh_last; /* addr of last next element */         \
+}
+
+#define        SIMPLEQ_HEAD_INITIALIZER(head)                                  \
+       { NULL, &(head).sqh_first }
+
+#define        SIMPLEQ_ENTRY(type)                                             \
+struct {                                                               \
+       struct type *sqe_next;  /* next element */                      \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define        SIMPLEQ_FIRST(head)             ((head)->sqh_first)
+#define        SIMPLEQ_END(head)               NULL
+#define        SIMPLEQ_EMPTY(head)             ((head)->sqh_first == SIMPLEQ_END(head))
+#define        SIMPLEQ_NEXT(elm, field)        ((elm)->field.sqe_next)
+
+#define        SIMPLEQ_FOREACH(var, head, field)                               \
+       for ((var) = ((head)->sqh_first);                               \
+           (var) != SIMPLEQ_END(head);                                 \
+           (var) = ((var)->field.sqe_next))
+
+#define        SIMPLEQ_FOREACH_SAFE(var, head, field, next)                    \
+       for ((var) = ((head)->sqh_first);                               \
+           (var) != SIMPLEQ_END(head) &&                               \
+           ((next = ((var)->field.sqe_next)), 1);                      \
+           (var) = (next))
+
+/*
+ * Simple queue functions.
+ */
+#define        SIMPLEQ_INIT(head) do {                                         \
+       (head)->sqh_first = NULL;                                       \
+       (head)->sqh_last = &(head)->sqh_first;                          \
+} while (/*CONSTCOND*/0)
+
+#define        SIMPLEQ_INSERT_HEAD(head, elm, field) do {                      \
+       if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)        \
+               (head)->sqh_last = &(elm)->field.sqe_next;              \
+       (head)->sqh_first = (elm);                                      \
+} while (/*CONSTCOND*/0)
+
+#define        SIMPLEQ_INSERT_TAIL(head, elm, field) do {                      \
+       (elm)->field.sqe_next = NULL;                                   \
+       *(head)->sqh_last = (elm);                                      \
+       (head)->sqh_last = &(elm)->field.sqe_next;                      \
+} while (/*CONSTCOND*/0)
+
+#define        SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {            \
+       if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+               (head)->sqh_last = &(elm)->field.sqe_next;              \
+       (listelm)->field.sqe_next = (elm);                              \
+} while (/*CONSTCOND*/0)
+
+#define        SIMPLEQ_REMOVE_HEAD(head, field) do {                           \
+       if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+               (head)->sqh_last = &(head)->sqh_first;                  \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do {                    \
+       if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
+           == NULL)                                                    \
+               (head)->sqh_last = &(elm)->field.sqe_next;              \
+} while (/*CONSTCOND*/0)
+
+#define        SIMPLEQ_REMOVE(head, elm, type, field) do {                     \
+       if ((head)->sqh_first == (elm)) {                               \
+               SIMPLEQ_REMOVE_HEAD((head), field);                     \
+       } else {                                                        \
+               struct type *curelm = (head)->sqh_first;                \
+               while (curelm->field.sqe_next != (elm))                 \
+                       curelm = curelm->field.sqe_next;                \
+               if ((curelm->field.sqe_next =                           \
+                       curelm->field.sqe_next->field.sqe_next) == NULL) \
+                           (head)->sqh_last = &(curelm)->field.sqe_next; \
+       }                                                               \
+} while (/*CONSTCOND*/0)
+
+#define        SIMPLEQ_CONCAT(head1, head2) do {                               \
+       if (!SIMPLEQ_EMPTY((head2))) {                                  \
+               *(head1)->sqh_last = (head2)->sqh_first;                \
+               (head1)->sqh_last = (head2)->sqh_last;          \
+               SIMPLEQ_INIT((head2));                                  \
+       }                                                               \
+} while (/*CONSTCOND*/0)
+
+#define        SIMPLEQ_LAST(head, type, field)                                 \
+       (SIMPLEQ_EMPTY((head)) ?                                                \
+               NULL :                                                  \
+               ((struct type *)(void *)                                \
+               ((char *)((head)->sqh_last) - offsetof(struct type, field))))
+
+/*
+ * Tail queue definitions.
+ */
+#define        _TAILQ_HEAD(name, type, qual)                                   \
+struct name {                                                          \
+       qual type *tqh_first;           /* first element */             \
+       qual type *qual *tqh_last;      /* addr of last next element */ \
+}
+#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,)
+
+#define        TAILQ_HEAD_INITIALIZER(head)                                    \
+       { TAILQ_END(head), &(head).tqh_first }
+
+#define        _TAILQ_ENTRY(type, qual)                                        \
+struct {                                                               \
+       qual type *tqe_next;            /* next element */              \
+       qual type *qual *tqe_prev;      /* address of previous next element */\
+}
+#define TAILQ_ENTRY(type)      _TAILQ_ENTRY(struct type,)
+
+/*
+ * Tail queue access methods.
+ */
+#define        TAILQ_FIRST(head)               ((head)->tqh_first)
+#define        TAILQ_END(head)                 (NULL)
+#define        TAILQ_NEXT(elm, field)          ((elm)->field.tqe_next)
+#define        TAILQ_LAST(head, headname) \
+       (*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define        TAILQ_PREV(elm, headname, field) \
+       (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define        TAILQ_EMPTY(head)               (TAILQ_FIRST(head) == TAILQ_END(head))
+
+
+#define        TAILQ_FOREACH(var, head, field)                                 \
+       for ((var) = ((head)->tqh_first);                               \
+           (var) != TAILQ_END(head);                                   \
+           (var) = ((var)->field.tqe_next))
+
+#define        TAILQ_FOREACH_SAFE(var, head, field, next)                      \
+       for ((var) = ((head)->tqh_first);                               \
+           (var) != TAILQ_END(head) &&                                 \
+           ((next) = TAILQ_NEXT(var, field), 1); (var) = (next))
+
+#define        TAILQ_FOREACH_REVERSE(var, head, headname, field)               \
+       for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last));\
+           (var) != TAILQ_END(head);                                   \
+           (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
+
+#define        TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev)    \
+       for ((var) = TAILQ_LAST((head), headname);                      \
+           (var) != TAILQ_END(head) &&                                 \
+           ((prev) = TAILQ_PREV((var), headname, field), 1); (var) = (prev))
+
+/*
+ * Tail queue functions.
+ */
+#if defined(QUEUEDEBUG)
+#define        QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field)                  \
+       if ((head)->tqh_first &&                                        \
+           (head)->tqh_first->field.tqe_prev != &(head)->tqh_first)    \
+               QUEUEDEBUG_ABORT("TAILQ_INSERT_HEAD %p %s:%d", (head),  \
+                   __FILE__, __LINE__);
+#define        QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field)                  \
+       if (*(head)->tqh_last != NULL)                                  \
+               QUEUEDEBUG_ABORT("TAILQ_INSERT_TAIL %p %s:%d", (head),  \
+                   __FILE__, __LINE__);
+#define        QUEUEDEBUG_TAILQ_OP(elm, field)                                 \
+       if ((elm)->field.tqe_next &&                                    \
+           (elm)->field.tqe_next->field.tqe_prev !=                    \
+           &(elm)->field.tqe_next)                                     \
+               QUEUEDEBUG_ABORT("TAILQ_* forw %p %s:%d", (elm),        \
+                   __FILE__, __LINE__);                                \
+       if (*(elm)->field.tqe_prev != (elm))                            \
+               QUEUEDEBUG_ABORT("TAILQ_* back %p %s:%d", (elm),        \
+                   __FILE__, __LINE__);
+#define        QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field)                    \
+       if ((elm)->field.tqe_next == NULL &&                            \
+           (head)->tqh_last != &(elm)->field.tqe_next)                 \
+               QUEUEDEBUG_ABORT("TAILQ_PREREMOVE head %p elm %p %s:%d",\
+                   (head), (elm), __FILE__, __LINE__);
+#define        QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field)                         \
+       (elm)->field.tqe_next = (void *)1L;                             \
+       (elm)->field.tqe_prev = (void *)1L;
+#else
+#define        QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field)
+#define        QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field)
+#define        QUEUEDEBUG_TAILQ_OP(elm, field)
+#define        QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field)
+#define        QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field)
+#endif
+
+#define        TAILQ_INIT(head) do {                                           \
+       (head)->tqh_first = TAILQ_END(head);                            \
+       (head)->tqh_last = &(head)->tqh_first;                          \
+} while (/*CONSTCOND*/0)
+
+#define        TAILQ_INSERT_HEAD(head, elm, field) do {                        \
+       QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field)              \
+       if (((elm)->field.tqe_next = (head)->tqh_first) != TAILQ_END(head))\
+               (head)->tqh_first->field.tqe_prev =                     \
+                   &(elm)->field.tqe_next;                             \
+       else                                                            \
+               (head)->tqh_last = &(elm)->field.tqe_next;              \
+       (head)->tqh_first = (elm);                                      \
+       (elm)->field.tqe_prev = &(head)->tqh_first;                     \
+} while (/*CONSTCOND*/0)
+
+#define        TAILQ_INSERT_TAIL(head, elm, field) do {                        \
+       QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field)              \
+       (elm)->field.tqe_next = TAILQ_END(head);                        \
+       (elm)->field.tqe_prev = (head)->tqh_last;                       \
+       *(head)->tqh_last = (elm);                                      \
+       (head)->tqh_last = &(elm)->field.tqe_next;                      \
+} while (/*CONSTCOND*/0)
+
+#define        TAILQ_INSERT_AFTER(head, listelm, elm, field) do {              \
+       QUEUEDEBUG_TAILQ_OP((listelm), field)                           \
+       if (((elm)->field.tqe_next = (listelm)->field.tqe_next) !=      \
+           TAILQ_END(head))                                            \
+               (elm)->field.tqe_next->field.tqe_prev =                 \
+                   &(elm)->field.tqe_next;                             \
+       else                                                            \
+               (head)->tqh_last = &(elm)->field.tqe_next;              \
+       (listelm)->field.tqe_next = (elm);                              \
+       (elm)->field.tqe_prev = &(listelm)->field.tqe_next;             \
+} while (/*CONSTCOND*/0)
+
+#define        TAILQ_INSERT_BEFORE(listelm, elm, field) do {                   \
+       QUEUEDEBUG_TAILQ_OP((listelm), field)                           \
+       (elm)->field.tqe_prev = (listelm)->field.tqe_prev;              \
+       (elm)->field.tqe_next = (listelm);                              \
+       *(listelm)->field.tqe_prev = (elm);                             \
+       (listelm)->field.tqe_prev = &(elm)->field.tqe_next;             \
+} while (/*CONSTCOND*/0)
+
+#define        TAILQ_REMOVE(head, elm, field) do {                             \
+       QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field)                \
+       QUEUEDEBUG_TAILQ_OP((elm), field)                               \
+       if (((elm)->field.tqe_next) != TAILQ_END(head))                 \
+               (elm)->field.tqe_next->field.tqe_prev =                 \
+                   (elm)->field.tqe_prev;                              \
+       else                                                            \
+               (head)->tqh_last = (elm)->field.tqe_prev;               \
+       *(elm)->field.tqe_prev = (elm)->field.tqe_next;                 \
+       QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field);                      \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do {                     \
+        if (((elm2)->field.tqe_next = (elm)->field.tqe_next) !=        \
+           TAILQ_END(head))                                            \
+                (elm2)->field.tqe_next->field.tqe_prev =               \
+                    &(elm2)->field.tqe_next;                           \
+        else                                                           \
+                (head)->tqh_last = &(elm2)->field.tqe_next;            \
+        (elm2)->field.tqe_prev = (elm)->field.tqe_prev;                        \
+        *(elm2)->field.tqe_prev = (elm2);                              \
+       QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field);                      \
+} while (/*CONSTCOND*/0)
+
+#define        TAILQ_CONCAT(head1, head2, field) do {                          \
+       if (!TAILQ_EMPTY(head2)) {                                      \
+               *(head1)->tqh_last = (head2)->tqh_first;                \
+               (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+               (head1)->tqh_last = (head2)->tqh_last;                  \
+               TAILQ_INIT((head2));                                    \
+       }                                                               \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define        STAILQ_HEAD(name, type)                                         \
+struct name {                                                          \
+       struct type *stqh_first;        /* first element */             \
+       struct type **stqh_last;        /* addr of last next element */ \
+}
+
+#define        STAILQ_HEAD_INITIALIZER(head)                                   \
+       { NULL, &(head).stqh_first }
+
+#define        STAILQ_ENTRY(type)                                              \
+struct {                                                               \
+       struct type *stqe_next; /* next element */                      \
+}
+
+/*
+ * Singly-linked Tail queue access methods.
+ */
+#define        STAILQ_FIRST(head)      ((head)->stqh_first)
+#define        STAILQ_END(head)        NULL
+#define        STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+#define        STAILQ_EMPTY(head)      (STAILQ_FIRST(head) == STAILQ_END(head))
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define        STAILQ_INIT(head) do {                                          \
+       (head)->stqh_first = NULL;                                      \
+       (head)->stqh_last = &(head)->stqh_first;                                \
+} while (/*CONSTCOND*/0)
+
+#define        STAILQ_INSERT_HEAD(head, elm, field) do {                       \
+       if (((elm)->field.stqe_next = (head)->stqh_first) == NULL)      \
+               (head)->stqh_last = &(elm)->field.stqe_next;            \
+       (head)->stqh_first = (elm);                                     \
+} while (/*CONSTCOND*/0)
+
+#define        STAILQ_INSERT_TAIL(head, elm, field) do {                       \
+       (elm)->field.stqe_next = NULL;                                  \
+       *(head)->stqh_last = (elm);                                     \
+       (head)->stqh_last = &(elm)->field.stqe_next;                    \
+} while (/*CONSTCOND*/0)
+
+#define        STAILQ_INSERT_AFTER(head, listelm, elm, field) do {             \
+       if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
+               (head)->stqh_last = &(elm)->field.stqe_next;            \
+       (listelm)->field.stqe_next = (elm);                             \
+} while (/*CONSTCOND*/0)
+
+#define        STAILQ_REMOVE_HEAD(head, field) do {                            \
+       if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \
+               (head)->stqh_last = &(head)->stqh_first;                        \
+} while (/*CONSTCOND*/0)
+
+#define        STAILQ_REMOVE(head, elm, type, field) do {                      \
+       if ((head)->stqh_first == (elm)) {                              \
+               STAILQ_REMOVE_HEAD((head), field);                      \
+       } else {                                                        \
+               struct type *curelm = (head)->stqh_first;               \
+               while (curelm->field.stqe_next != (elm))                        \
+                       curelm = curelm->field.stqe_next;               \
+               if ((curelm->field.stqe_next =                          \
+                       curelm->field.stqe_next->field.stqe_next) == NULL) \
+                           (head)->stqh_last = &(curelm)->field.stqe_next; \
+       }                                                               \
+} while (/*CONSTCOND*/0)
+
+#define        STAILQ_FOREACH(var, head, field)                                \
+       for ((var) = ((head)->stqh_first);                              \
+               (var);                                                  \
+               (var) = ((var)->field.stqe_next))
+
+#define        STAILQ_FOREACH_SAFE(var, head, field, tvar)                     \
+       for ((var) = STAILQ_FIRST((head));                              \
+           (var) && ((tvar) = STAILQ_NEXT((var), field), 1);           \
+           (var) = (tvar))
+
+#define        STAILQ_CONCAT(head1, head2) do {                                \
+       if (!STAILQ_EMPTY((head2))) {                                   \
+               *(head1)->stqh_last = (head2)->stqh_first;              \
+               (head1)->stqh_last = (head2)->stqh_last;                \
+               STAILQ_INIT((head2));                                   \
+       }                                                               \
+} while (/*CONSTCOND*/0)
+
+#define        STAILQ_LAST(head, type, field)                                  \
+       (STAILQ_EMPTY((head)) ?                                         \
+               NULL :                                                  \
+               ((struct type *)(void *)                                \
+               ((char *)((head)->stqh_last) - offsetof(struct type, field))))
+
+
+#ifndef _KERNEL
+/*
+ * Circular queue definitions. Do not use. We still keep the macros
+ * for compatibility but because of pointer aliasing issues their use
+ * is discouraged!
+ */
+
+/*
+ * __launder_type():  We use this ugly hack to work around the the compiler
+ * noticing that two types may not alias each other and elide tests in code.
+ * We hit this in the CIRCLEQ macros when comparing 'struct name *' and
+ * 'struct type *' (see CIRCLEQ_HEAD()).  Modern compilers (such as GCC
+ * 4.8) declare these comparisons as always false, causing the code to
+ * not run as designed.
+ *
+ * This hack is only to be used for comparisons and thus can be fully const.
+ * Do not use for assignment.
+ *
+ * If we ever choose to change the ABI of the CIRCLEQ macros, we could fix
+ * this by changing the head/tail sentinal values, but see the note above
+ * this one.
+ */
+static __inline const void * __launder_type(const void *);
+static __inline const void *
+__launder_type(const void *__x)
+{
+       __asm __volatile("" : "+r" (__x));
+       return __x;
+}
+
+#if defined(QUEUEDEBUG)
+#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field)                           \
+       if ((head)->cqh_first != CIRCLEQ_ENDC(head) &&                  \
+           (head)->cqh_first->field.cqe_prev != CIRCLEQ_ENDC(head))    \
+               QUEUEDEBUG_ABORT("CIRCLEQ head forw %p %s:%d", (head),  \
+                     __FILE__, __LINE__);                              \
+       if ((head)->cqh_last != CIRCLEQ_ENDC(head) &&                   \
+           (head)->cqh_last->field.cqe_next != CIRCLEQ_ENDC(head))     \
+               QUEUEDEBUG_ABORT("CIRCLEQ head back %p %s:%d", (head),  \
+                     __FILE__, __LINE__);
+#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field)                       \
+       if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) {              \
+               if ((head)->cqh_last != (elm))                          \
+                       QUEUEDEBUG_ABORT("CIRCLEQ elm last %p %s:%d",   \
+                           (elm), __FILE__, __LINE__);                 \
+       } else {                                                        \
+               if ((elm)->field.cqe_next->field.cqe_prev != (elm))     \
+                       QUEUEDEBUG_ABORT("CIRCLEQ elm forw %p %s:%d",   \
+                           (elm), __FILE__, __LINE__);                 \
+       }                                                               \
+       if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) {              \
+               if ((head)->cqh_first != (elm))                         \
+                       QUEUEDEBUG_ABORT("CIRCLEQ elm first %p %s:%d",  \
+                           (elm), __FILE__, __LINE__);                 \
+       } else {                                                        \
+               if ((elm)->field.cqe_prev->field.cqe_next != (elm))     \
+                       QUEUEDEBUG_ABORT("CIRCLEQ elm prev %p %s:%d",   \
+                           (elm), __FILE__, __LINE__);                 \
+       }
+#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field)                      \
+       (elm)->field.cqe_next = (void *)1L;                             \
+       (elm)->field.cqe_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field)
+#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field)
+#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field)
+#endif
+
+#define        CIRCLEQ_HEAD(name, type)                                        \
+struct name {                                                          \
+       struct type *cqh_first;         /* first element */             \
+       struct type *cqh_last;          /* last element */              \
+}
+
+#define        CIRCLEQ_HEAD_INITIALIZER(head)                                  \
+       { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define        CIRCLEQ_ENTRY(type)                                             \
+struct {                                                               \
+       struct type *cqe_next;          /* next element */              \
+       struct type *cqe_prev;          /* previous element */          \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define        CIRCLEQ_INIT(head) do {                                         \
+       (head)->cqh_first = CIRCLEQ_END(head);                          \
+       (head)->cqh_last = CIRCLEQ_END(head);                           \
+} while (/*CONSTCOND*/0)
+
+#define        CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {            \
+       QUEUEDEBUG_CIRCLEQ_HEAD((head), field)                          \
+       QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field)                \
+       (elm)->field.cqe_next = (listelm)->field.cqe_next;              \
+       (elm)->field.cqe_prev = (listelm);                              \
+       if ((listelm)->field.cqe_next == CIRCLEQ_ENDC(head))            \
+               (head)->cqh_last = (elm);                               \
+       else                                                            \
+               (listelm)->field.cqe_next->field.cqe_prev = (elm);      \
+       (listelm)->field.cqe_next = (elm);                              \
+} while (/*CONSTCOND*/0)
+
+#define        CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {           \
+       QUEUEDEBUG_CIRCLEQ_HEAD((head), field)                          \
+       QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field)                \
+       (elm)->field.cqe_next = (listelm);                              \
+       (elm)->field.cqe_prev = (listelm)->field.cqe_prev;              \
+       if ((listelm)->field.cqe_prev == CIRCLEQ_ENDC(head))            \
+               (head)->cqh_first = (elm);                              \
+       else                                                            \
+               (listelm)->field.cqe_prev->field.cqe_next = (elm);      \
+       (listelm)->field.cqe_prev = (elm);                              \
+} while (/*CONSTCOND*/0)
+
+#define        CIRCLEQ_INSERT_HEAD(head, elm, field) do {                      \
+       QUEUEDEBUG_CIRCLEQ_HEAD((head), field)                          \
+       (elm)->field.cqe_next = (head)->cqh_first;                      \
+       (elm)->field.cqe_prev = CIRCLEQ_END(head);                      \
+       if ((head)->cqh_last == CIRCLEQ_ENDC(head))                     \
+               (head)->cqh_last = (elm);                               \
+       else                                                            \
+               (head)->cqh_first->field.cqe_prev = (elm);              \
+       (head)->cqh_first = (elm);                                      \
+} while (/*CONSTCOND*/0)
+
+#define        CIRCLEQ_INSERT_TAIL(head, elm, field) do {                      \
+       QUEUEDEBUG_CIRCLEQ_HEAD((head), field)                          \
+       (elm)->field.cqe_next = CIRCLEQ_END(head);                      \
+       (elm)->field.cqe_prev = (head)->cqh_last;                       \
+       if ((head)->cqh_first == CIRCLEQ_ENDC(head))                    \
+               (head)->cqh_first = (elm);                              \
+       else                                                            \
+               (head)->cqh_last->field.cqe_next = (elm);               \
+       (head)->cqh_last = (elm);                                       \
+} while (/*CONSTCOND*/0)
+
+#define        CIRCLEQ_REMOVE(head, elm, field) do {                           \
+       QUEUEDEBUG_CIRCLEQ_HEAD((head), field)                          \
+       QUEUEDEBUG_CIRCLEQ_ELM((head), (elm), field)                    \
+       if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head))                \
+               (head)->cqh_last = (elm)->field.cqe_prev;               \
+       else                                                            \
+               (elm)->field.cqe_next->field.cqe_prev =                 \
+                   (elm)->field.cqe_prev;                              \
+       if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head))                \
+               (head)->cqh_first = (elm)->field.cqe_next;              \
+       else                                                            \
+               (elm)->field.cqe_prev->field.cqe_next =                 \
+                   (elm)->field.cqe_next;                              \
+       QUEUEDEBUG_CIRCLEQ_POSTREMOVE((elm), field)                     \
+} while (/*CONSTCOND*/0)
+
+#define        CIRCLEQ_FOREACH(var, head, field)                               \
+       for ((var) = ((head)->cqh_first);                               \
+               (var) != CIRCLEQ_ENDC(head);                            \
+               (var) = ((var)->field.cqe_next))
+
+#define        CIRCLEQ_FOREACH_REVERSE(var, head, field)                       \
+       for ((var) = ((head)->cqh_last);                                \
+               (var) != CIRCLEQ_ENDC(head);                            \
+               (var) = ((var)->field.cqe_prev))
+
+/*
+ * Circular queue access methods.
+ */
+#define        CIRCLEQ_FIRST(head)             ((head)->cqh_first)
+#define        CIRCLEQ_LAST(head)              ((head)->cqh_last)
+/* For comparisons */
+#define        CIRCLEQ_ENDC(head)              (__launder_type(head))
+/* For assignments */
+#define        CIRCLEQ_END(head)               ((void *)(head))
+#define        CIRCLEQ_NEXT(elm, field)        ((elm)->field.cqe_next)
+#define        CIRCLEQ_PREV(elm, field)        ((elm)->field.cqe_prev)
+#define        CIRCLEQ_EMPTY(head)                                             \
+    (CIRCLEQ_FIRST(head) == CIRCLEQ_ENDC(head))
+
+#define CIRCLEQ_LOOP_NEXT(head, elm, field)                            \
+       (((elm)->field.cqe_next == CIRCLEQ_ENDC(head))                  \
+           ? ((head)->cqh_first)                                       \
+           : (elm->field.cqe_next))
+#define CIRCLEQ_LOOP_PREV(head, elm, field)                            \
+       (((elm)->field.cqe_prev == CIRCLEQ_ENDC(head))                  \
+           ? ((head)->cqh_last)                                        \
+           : (elm->field.cqe_prev))
+#endif /* !_KERNEL */
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/arch/avr/include/sys/types.h b/arch/avr/include/sys/types.h
new file mode 100644 (file)
index 0000000..51403a3
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_SYS_TYPES_H_
+#define UCG_SYS_TYPES_H_
+
+typedef intptr_t ssize_t;
+
+#endif /* UCG_SYS_TYPES_H_ */
diff --git a/arch/avr/include/ucg_delay.h b/arch/avr/include/ucg_delay.h
new file mode 100644 (file)
index 0000000..59d18c9
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_DELAY_H_
+#define UCG_DELAY_H_
+
+#include <util/delay.h>
+
+#include <stdint.h>
+
+/**
+ * Loop during *ms* milliseconds
+ */
+static inline void ucg_delay_ms(uint16_t ms)
+{
+       _delay_ms(ms);
+}
+
+#endif /* UCG_DELAY_H_ */
diff --git a/arch/avr/include/ucg_irq.h b/arch/avr/include/ucg_irq.h
new file mode 100644 (file)
index 0000000..4505881
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_IRQ_H_
+#define UCG_IRQ_H_
+
+#include <avr/interrupt.h>
+
+typedef uint8_t ucg_irqflags_t;
+
+static inline void ucg_irq_lock(void)
+{
+       cli();
+}
+
+static inline void ucg_irq_unlock(void)
+{
+       sei();
+}
+
+static inline ucg_irqflags_t ucg_irq_lock_save(void)
+{
+       ucg_irqflags_t flags;
+
+       flags = SREG;
+       cli();
+       return flags;
+}
+
+static inline void ucg_irq_unlock_restore(ucg_irqflags_t flags)
+{
+       SREG = flags;
+}
+
+static inline int ucg_irq_locked(void)
+{
+       return !(bit_is_set(SREG,7));
+}
+
+#endif /* UCG_IRQ_H_ */
diff --git a/arch/avr/include/ucg_reent_intr.h b/arch/avr/include/ucg_reent_intr.h
new file mode 100644 (file)
index 0000000..b253f74
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016, Fabrice DESCLAUX <serpilliere@droids-corp.org>
+ * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_REENT_INTR_H_
+#define UCG_REENT_INTR_H_
+
+#define UCG_REENT_INTR(f) f()
+
+#endif
diff --git a/arch/avr/include/unistd.h b/arch/avr/include/unistd.h
new file mode 100644 (file)
index 0000000..d949999
--- /dev/null
@@ -0,0 +1 @@
+/* empty, just have it for compat */
diff --git a/arch/avr/mk/ucgine-arch.mk b/arch/avr/mk/ucgine-arch.mk
new file mode 100644 (file)
index 0000000..50e81c1
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+ARCH_CFLAGS += -Wall -W
+ARCH_CFLAGS += -I$(UCGINE)/arch/$(UCGINE_ARCH)/include
+ARCH_CFLAGS += -Os
+ARCH_CFLAGS += -ffunction-sections
+
+ARCH_LDFLAGS += -Wl,--gc-sections
+
+ARCH_CROSS := avr-
diff --git a/arch/avr/uart/include/ucg_avr_uart.h b/arch/avr/uart/include/ucg_avr_uart.h
new file mode 100644 (file)
index 0000000..400b6cf
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UGC_AVR_UART_H_
+#define UGC_AVR_UART_H_
+
+#include <ucg_uart.h>
+
+typedef volatile uint8_t *uart_reg_t;
+
+struct ucg_avr_uart {
+                                       /* Example: */
+       uart_reg_t reg_udr;             /* &UDR0 */
+       uart_reg_t reg_ucsra;           /* &UCSR0A */
+       uart_reg_t reg_ucsrb;           /* &UCSR0B */
+       uart_reg_t reg_ucsrc;           /* &UCSR0C */
+       uart_reg_t reg_ubrrl;           /* &UBRR0L */
+       uart_reg_t reg_ubrrh;           /* &UBRR0H */
+
+       uint32_t bit_udre:3;            /* UDRE0 */
+       uint32_t bit_rxc:3;             /* RXC0 */
+       uint32_t bit_udrie:3;           /* UDRIE0 */
+       uint32_t bit_txen:3;            /* RXEN0 */
+       uint32_t bit_rxen:3;            /* TXEN0 */
+       uint32_t bit_rxcie:3;           /* RXCIE0 */
+       uint32_t bit_u2x:3;             /* U2X0 */
+};
+
+const struct ucg_uart_driver_ops avr_uart_ops;
+
+#endif /* UGC_AVR_UART_H_ */
diff --git a/arch/avr/uart/ucg_avr_uart.c b/arch/avr/uart/ucg_avr_uart.c
new file mode 100644 (file)
index 0000000..8c239f4
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <avr/io.h>
+#include <ucg_uart.h>
+#include <ucg_avr_uart.h>
+
+static void disable_tx_irq(struct ucg_uart *uart)
+{
+       struct ucg_avr_uart *avr_uart = uart->driver_data;
+       *avr_uart->reg_ucsrb &= ~(1 << avr_uart->bit_udrie);
+}
+
+static void enable_tx_irq(struct ucg_uart *uart)
+{
+       struct ucg_avr_uart *avr_uart = uart->driver_data;
+       *avr_uart->reg_ucsrb |= (1 << avr_uart->bit_udrie);
+}
+
+static uint8_t tx_ready(struct ucg_uart *uart)
+{
+       struct ucg_avr_uart *avr_uart = uart->driver_data;
+       return !!(*avr_uart->reg_ucsra & (1 << avr_uart->bit_udre));
+}
+
+static uint8_t rx_ready(struct ucg_uart *uart)
+{
+       struct ucg_avr_uart *avr_uart = uart->driver_data;
+       return !!(*avr_uart->reg_ucsra & (1 << avr_uart->bit_rxc));
+}
+
+static void set_udr(struct ucg_uart *uart, char c)
+{
+       struct ucg_avr_uart *avr_uart = uart->driver_data;
+       *avr_uart->reg_udr = c;
+}
+
+static char get_udr(struct ucg_uart *uart)
+{
+       struct ucg_avr_uart *avr_uart = uart->driver_data;
+       return *avr_uart->reg_udr;
+}
+
+static int set_conf(struct ucg_uart *uart, const struct ucg_uart_config *conf)
+{
+       struct ucg_avr_uart *avr_uart = uart->driver_data;
+       uint16_t baudreg;
+       uint8_t lo, hi;
+       uint8_t use_u2x = 1; /* always use double speed */
+
+       if (conf->enable == 0) {
+               *avr_uart->reg_ucsrb = 0;
+               return 0;
+       }
+
+       if (use_u2x)
+               baudreg = (F_CPU / (conf->baudrate * 8UL)) - 1;
+       else
+               baudreg = (F_CPU / (conf->baudrate * 16UL)) - 1;
+
+       lo = (uint8_t)baudreg;
+       hi = (uint8_t)((baudreg >> 8) & 0xF);
+
+       *avr_uart->reg_ubrrl = lo;
+       *avr_uart->reg_ubrrh = hi;
+
+       *avr_uart->reg_ucsra = (
+               (use_u2x << avr_uart->bit_u2x));
+       *avr_uart->reg_ucsrb = (
+               (1 << avr_uart->bit_txen) |
+               (1 << avr_uart->bit_rxen) |
+               (1 << avr_uart->bit_rxcie));
+
+       return 0;
+}
+
+const struct ucg_uart_driver_ops avr_uart_ops = {
+       .disable_tx_irq = disable_tx_irq,
+       .enable_tx_irq = enable_tx_irq,
+       .tx_ready = tx_ready,
+       .rx_ready = rx_ready,
+       .set_udr = set_udr,
+       .get_udr = get_udr,
+       .set_conf = set_conf,
+};
diff --git a/arch/posix/include/ucg_delay.h b/arch/posix/include/ucg_delay.h
new file mode 100644 (file)
index 0000000..a8e7fa3
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_DELAY_H_
+#define UCG_DELAY_H_
+
+#include <stdint.h>
+#include <unistd.h>
+
+/**
+ * Loop during *ms* milliseconds
+ */
+static inline void ucg_delay_ms(uint16_t ms)
+{
+       (void)ms;
+       //usleep(ms * 1000);
+}
+
+#endif /* UCG_DELAY_H_ */
diff --git a/arch/posix/include/ucg_irq.h b/arch/posix/include/ucg_irq.h
new file mode 100644 (file)
index 0000000..435f7e5
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_IRQ_H_
+#define UCG_IRQ_H_
+
+#include <stdint.h>
+
+typedef uint8_t ucg_irqflags_t;
+
+static inline void ucg_irq_lock(void)
+{
+}
+
+static inline void ucg_irq_unlock(void)
+{
+}
+
+static inline ucg_irqflags_t ucg_irq_lock_save(void)
+{
+       return 0;
+}
+
+static inline void ucg_irq_unlock_restore(ucg_irqflags_t flags)
+{
+       (void)flags;
+}
+
+static inline int ucg_irq_locked(void)
+{
+       return 0;
+}
+
+#endif /* UCG_IRQ_H_ */
diff --git a/arch/posix/mk/ucgine-arch.mk b/arch/posix/mk/ucgine-arch.mk
new file mode 100644 (file)
index 0000000..61d64de
--- /dev/null
@@ -0,0 +1,30 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+ARCH_CFLAGS += -Wall -W
+ARCH_CFLAGS += -I$(UCGINE)/arch/$(UCGINE_ARCH)/include
+ARCH_CFLAGS += -O3
diff --git a/arch/stm32/include/ucg_delay.h b/arch/stm32/include/ucg_delay.h
new file mode 100644 (file)
index 0000000..3022936
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_DELAY_H_
+#define UCG_DELAY_H_
+
+#include <stdint.h>
+
+/**
+ * Loop during *ms* milliseconds
+ */
+static inline void ucg_delay_ms(uint16_t ms)
+{
+       /* XXX use F_CPU */
+       while (ms-- > 0) {
+               volatile int x = 5971;
+               while (x-- > 0)
+                       __asm("nop");
+       }
+}
+
+#endif /* UCG_DELAY_H_ */
diff --git a/arch/stm32/include/ucg_irq.h b/arch/stm32/include/ucg_irq.h
new file mode 100644 (file)
index 0000000..4245e83
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_IRQ_H_
+#define UCG_IRQ_H_
+
+#include <stdint.h>
+
+typedef uint32_t ucg_irqflags_t;
+
+static inline uint32_t __ucg_get_primask(void)
+{
+       ucg_irqflags_t primask_reg;
+
+       asm volatile (
+               "mrs %0, primask\n"
+               : "=r" (primask_reg)
+               :
+               : );
+
+       return primask_reg;
+}
+
+static inline void __ucg_set_primask(ucg_irqflags_t primask_reg)
+{
+       asm volatile (
+               "msr primask, %0\n"
+               :
+               : "r" (primask_reg)
+               : );
+}
+
+static inline void ucg_irq_lock(void)
+{
+       __asm volatile ("cpsid i");
+}
+
+static inline void ucg_irq_unlock(void)
+{
+       __asm volatile ("cpsie i");
+}
+
+static inline ucg_irqflags_t ucg_irq_lock_save(void)
+{
+       ucg_irqflags_t flags;
+
+       flags = __ucg_get_primask();
+       ucg_irq_lock();
+       return flags;
+}
+
+static inline void ucg_irq_unlock_restore(ucg_irqflags_t flags)
+{
+       __ucg_set_primask(flags);
+}
+
+static inline int ucg_irq_locked(void)
+{
+       ucg_irqflags_t flags = __ucg_get_primask();
+       return !!flags;
+}
+
+#endif /* UCG_IRQ_H_ */
diff --git a/arch/stm32/include/ucg_reent_intr.h b/arch/stm32/include/ucg_reent_intr.h
new file mode 100644 (file)
index 0000000..99fd578
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2016, Fabrice DESCLAUX <serpilliere@droids-corp.org>
+ * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_REENT_INTR_H_
+#define UCG_REENT_INTR_H_
+
+/* XXX doc */
+void ucg_reent_intr(uint32_t *context, void *fct);
+//void ucg_reent_intr(uint32_t *context);
+
+/*
+.align 4
+.long my_var
+*/
+#define UCG_REENT_INTR(f)                              \
+       __asm__ volatile (                              \
+               "SUB    SP, SP, 0x38     \n"            \
+               "STR    LR, [SP]         \n"            \
+               "ADD    R0, SP, 0x10     \n"            \
+               "LDR    R1, =" #f "      \n"            \
+               "BL     ucg_reent_intr   \n"            \
+               "POP    {LR}             \n"            \
+               "ADD    SP, SP, 0xC      \n"            \
+               "BX     LR               \n"            \
+               :                                       \
+               :                                       \
+               : /* No clobbers */                     \
+       )
+
+#define old_UCG_REENT_INTR(f)                          \
+       __asm__ volatile (                              \
+               "SUB    SP, SP, 0x38     \n"            \
+               "STR    LR, [SP]         \n"            \
+               "ADD    R0, SP, 0x10     \n"            \
+               "MOV    R1, %[value]     \n"            \
+               "BL     ucg_reent_intr   \n"            \
+               "POP    {LR}             \n"            \
+               "ADD    SP, SP, 0xC      \n"            \
+               "BX     LR               \n"            \
+               :                                       \
+               : [value]"r"   ((uint32_t)f)            \
+               : /* No clobbers */                     \
+       )
+
+#define XXX_UCG_REENT_INTR(f)                          \
+       __asm__ volatile (                              \
+               "SUB    SP, SP, 0x38     \n"            \
+               "STR    LR, [SP]         \n"            \
+               "ADD    R0, SP, 0x10     \n"            \
+               "BL     ucg_reent_intr   \n"            \
+               "POP    {LR}             \n"            \
+               "ADD    SP, SP, 0xC      \n"            \
+               "BX     LR               \n"            \
+       )
+
+#endif
diff --git a/arch/stm32/mk/ucgine-arch.mk b/arch/stm32/mk/ucgine-arch.mk
new file mode 100644 (file)
index 0000000..3967986
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+ARCH_CFLAGS += -I$(UCGINE)/arch/$(UCGINE_ARCH)/include
+ARCH_CFLAGS += -DUSE_STDPERIPH_DRIVER
+ARCH_CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
+ARCH_CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
+ARCH_CFLAGS += -ffunction-sections
+
+ARCH_LDFLAGS += -Wl,--gc-sections
+ARCH_LDFLAGS += --specs=rdimon.specs -lc
+ARCH_LDFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
+ARCH_LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
+
+ARCH_CROSS := arm-none-eabi-
diff --git a/arch/stm32/uart/include/ucg_stm32_uart.h b/arch/stm32/uart/include/ucg_stm32_uart.h
new file mode 100644 (file)
index 0000000..a98cc5d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UGC_STM32_UART_H_
+#define UGC_STM32_UART_H_
+
+#include <stm32f4xx.h>
+
+#include <ucg_uart.h>
+
+struct ucg_stm32_uart {
+                                               /* Example: */
+       USART_TypeDef *uart;                    /* USART2 */
+       uint32_t rcc_uart;                      /* RCC_APB1Periph_USART2 */
+       uint32_t rcc_gpio;                      /* RCC_AHB1Periph_GPIOA */
+       GPIO_TypeDef *gpio;                     /* GPIOA */
+       uint8_t gpio_af;                        /* GPIO_AF_USART2 */
+       uint16_t gpio_pins;                     /* GPIO_Pin_2 | GPIO_Pin_3 */
+       uint8_t gpio_speed;                     /* GPIO_Speed_25MHz */
+       uint8_t irq;                            /* USART2_IRQn */
+       uint8_t irq_preempt_prio;               /* 0 */
+       uint8_t irq_sub_prio;                   /* 0 */
+};
+
+const struct ucg_uart_driver_ops stm32_uart_ops;
+
+#endif /* UGC_STM32_UART_H_ */
diff --git a/arch/stm32/uart/ucg_stm32_uart.c b/arch/stm32/uart/ucg_stm32_uart.c
new file mode 100644 (file)
index 0000000..19de514
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <ucg_uart.h>
+#include <ucg_stm32_uart.h>
+
+static void disable_tx_irq(struct ucg_uart *uart)
+{
+       struct ucg_stm32_uart *stm32_uart = uart->driver_data;
+
+       stm32_uart->uart->SR &= ~(USART_FLAG_TXE);
+       stm32_uart->uart->CR1 &= ~(USART_CR1_TXEIE);
+}
+
+static void enable_tx_irq(struct ucg_uart *uart)
+{
+       struct ucg_stm32_uart *stm32_uart = uart->driver_data;
+
+       stm32_uart->uart->CR1 |= USART_CR1_TXEIE;
+}
+
+static uint8_t tx_ready(struct ucg_uart *uart)
+{
+       struct ucg_stm32_uart *stm32_uart = uart->driver_data;
+
+       return !!(stm32_uart->uart->SR & USART_FLAG_TXE);
+}
+
+static uint8_t rx_ready(struct ucg_uart *uart)
+{
+       struct ucg_stm32_uart *stm32_uart = uart->driver_data;
+
+       return !!(stm32_uart->uart->SR & USART_FLAG_RXNE);
+}
+
+static void set_udr(struct ucg_uart *uart, char c)
+{
+       struct ucg_stm32_uart *stm32_uart = uart->driver_data;
+
+       stm32_uart->uart->DR = c;
+}
+
+static char get_udr(struct ucg_uart *uart)
+{
+       struct ucg_stm32_uart *stm32_uart = uart->driver_data;
+
+       return stm32_uart->uart->DR;
+}
+
+static int set_conf(struct ucg_uart *uart, const struct ucg_uart_config *conf)
+{
+       USART_InitTypeDef u;
+       GPIO_InitTypeDef gpio;
+       NVIC_InitTypeDef nvic;
+       struct ucg_stm32_uart *stm32_uart = uart->driver_data;
+       int i;
+
+       /* even if it is asked to disable, keep the RCC enabled */
+
+       /* Enable the peripheral clock. */
+       RCC_APB1PeriphClockCmd(stm32_uart->rcc_uart, ENABLE);
+       __asm("dsb");
+
+       /* Enable the peripheral clock for GPIO. */
+       RCC_AHB1PeriphClockCmd(stm32_uart->rcc_gpio, ENABLE);
+       __asm("dsb");
+
+       if (conf->enable == 0) {
+               USART_ITConfig(stm32_uart->uart, USART_IT_TXE, DISABLE);
+               USART_ITConfig(stm32_uart->uart, USART_IT_RXNE, DISABLE);
+               USART_Cmd(stm32_uart->uart, ENABLE);
+
+               nvic.NVIC_IRQChannel = stm32_uart->irq;
+               nvic.NVIC_IRQChannelCmd = DISABLE;
+               NVIC_Init(&nvic);
+               return 0;
+       }
+
+       if (conf->nbits != 8)
+               return -EINVAL;
+
+       /* connect to alternate function */
+       for (i = 0; i < 16; i++) {
+               if ((1 << i) & stm32_uart->gpio_pins) {
+                       GPIO_PinAFConfig(stm32_uart->gpio, i,
+                               stm32_uart->gpio_af);
+               }
+       }
+
+       GPIO_StructInit(&gpio);
+       gpio.GPIO_Speed = stm32_uart->gpio_speed;
+
+       /* configure rx and tx */
+       gpio.GPIO_Pin = stm32_uart->gpio_pins;
+       gpio.GPIO_Mode = GPIO_Mode_AF;
+       GPIO_Init(stm32_uart->gpio, &gpio);
+
+       /* USART configuration */
+       u.USART_BaudRate = conf->baudrate;
+       u.USART_WordLength = USART_WordLength_8b;
+       u.USART_StopBits = USART_StopBits_1;
+       u.USART_Parity = USART_Parity_No;
+       u.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
+       u.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
+       USART_Init(stm32_uart->uart, &u);
+
+       /* Enable and set EXTI Interrupt to the defined priority */
+       nvic.NVIC_IRQChannel = stm32_uart->irq;
+       /* nvic.NVIC_IRQChannelPreemptionPriority = stm32_uart->irq_preempt_prio; */
+       /* nvic.NVIC_IRQChannelSubPriority = stm32_uart->irq_sub_prio; */
+       nvic.NVIC_IRQChannelCmd = ENABLE;
+       NVIC_Init(&nvic);
+
+       /* Enable RX interruption */
+       USART_ITConfig(stm32_uart->uart, USART_IT_RXNE, ENABLE);
+
+       /* Enable USART */
+       USART_Cmd(stm32_uart->uart, ENABLE);
+
+       return 0;
+}
+
+const struct ucg_uart_driver_ops stm32_uart_ops = {
+       .disable_tx_irq = disable_tx_irq,
+       .enable_tx_irq = enable_tx_irq,
+       .tx_ready = tx_ready,
+       .rx_ready = rx_ready,
+       .set_udr = set_udr,
+       .get_udr = get_udr,
+       .set_conf = set_conf,
+};
diff --git a/arch/stm32/ucg_reent_intr.c b/arch/stm32/ucg_reent_intr.c
new file mode 100644 (file)
index 0000000..b45439c
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2016, Fabrice DESCLAUX <serpilliere@droids-corp.org>
+ * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "ucg_reent_intr.h"
+
+struct saved_stack {
+       uint32_t r0;
+       uint32_t r1;
+       uint32_t r2;
+       uint32_t r3;
+       uint32_t r12;
+       uint32_t lr;
+       uint32_t pc;
+       uint32_t psr;
+};
+
+struct wrapper_stack {
+       uint32_t psr;
+       uint32_t lr;
+       uint32_t r0;
+       uint32_t r1;
+       uint32_t r2;
+       uint32_t r3;
+       uint32_t r12;
+       uint32_t pc;
+};
+
+__attribute__((naked)) uint32_t
+ret_wrapper_stkalign_float(void)
+{
+       __asm__ volatile (
+               /* restore float */
+               "ADD    SP, SP, 0x50       \n"
+               /* restore PSR, LR */
+               "POP   {R0, LR}        \n"
+               /* restore PSR */
+               //"MSR   PSR, R0       \n"
+               "msr APSR_nzcvq, R0 \n"
+               /* restore PSR, LR */
+               "POP   {R0-R3, R12, PC}        \n"
+       );
+}
+
+
+__attribute__((naked)) uint32_t
+ret_wrapper_stknoalign_float(void)
+{
+       __asm__ volatile (
+               /* restore float */
+               "ADD    SP, SP, 0x50       \n"
+               /* remove padding */
+               "ADD    SP, SP, 0x4       \n"
+               /* restore PSR, LR */
+               "POP   {R0, LR}        \n"
+               /* restore PSR */
+               //"MSR   PSR, R0       \n"
+               "msr APSR_nzcvq, R0 \n"
+               /* restore PSR, LR */
+               "POP   {R0-R3, R12, PC}        \n"
+       );
+}
+
+
+__attribute__((naked)) uint32_t
+ret_wrapper_stkalign_nofloat(void)
+{
+       __asm__ volatile (
+               /* restore float */
+               "ADD    SP, SP, 0x8       \n"
+               /* restore PSR, LR */
+               "POP   {R0, LR}        \n"
+               /* restore PSR */
+               //"MSR   PSR, R0       \n"
+               "msr APSR_nzcvq, R0 \n"
+               /* restore PSR, LR */
+               "POP   {R0-R3, R12, PC}        \n"
+       );
+}
+
+__attribute__((naked)) uint32_t
+ret_wrapper_stknoalign_nofloat(void)
+{
+       __asm__ volatile (
+               /* restore float */
+               "ADD    SP, SP, 0x8       \n"
+               /* remove padding */
+               "ADD    SP, SP, 0x4       \n"
+               /* restore PSR, LR */
+               "POP   {R0, LR}        \n"
+               /* restore PSR */
+               //"MSR   PSR, R0       \n"
+               "msr APSR_nzcvq, R0 \n"
+               /* restore PSR, LR */
+               "POP   {R0-R3, R12, PC}        \n"
+       );
+}
+
+void reent_intr(void);
+
+void ucg_reent_intr(uint32_t *context, void *fct)
+//void ucg_reent_intr(uint32_t *context)
+{
+       struct saved_stack *stk_new = (void *)context;
+       struct saved_stack *stk_old = (void *)(&context[10]);
+       uint32_t exe_return;
+       int stk_padding;
+       struct wrapper_stack wstack;
+
+       stk_padding = (stk_old->psr & 0x200)?1:0;
+
+       /* copy saved ctxt */
+       wstack.r0 = stk_old->r0;
+       wstack.r1 = stk_old->r1;
+       wstack.r2 = stk_old->r2;
+       wstack.r3 = stk_old->r3;
+       wstack.r12 = stk_old->r12;
+       wstack.lr = stk_old->lr;
+       wstack.psr = stk_old->psr & ~0x200;
+       wstack.pc = stk_old->pc | 1;
+
+       exe_return = (uint32_t)context[-4];
+
+
+       stk_new->pc = (uint32_t)fct;
+//     stk_new->pc = (uint32_t)reent_intr;
+       /* set arguments */
+       /* stk_new->r0 = 0; */
+       /* stk_new->r1 = 1; */
+       /* stk_new->r2 = 2; */
+       /* stk_new->r3 = 3; */
+       stk_new->r12 = wstack.r12;
+       stk_new->psr = 0x21000000;
+
+       if (exe_return & 0x10) {
+               /*  No Float stacked */
+               if (stk_padding) {
+                       stk_new->lr = (uint32_t)ret_wrapper_stknoalign_nofloat;
+                       memcpy(&context[10+1], &wstack, sizeof(wstack));
+               } else {
+                       memcpy(&context[10], &wstack, sizeof(wstack));
+                       stk_new->lr = (uint32_t)ret_wrapper_stkalign_nofloat;
+               }
+       } else {
+               /* Float stacked */
+               //STM_EVAL_LEDOn(LED7);
+               memmove(&context[10], &context[18], 18*4);
+               if (stk_padding) {
+                       //STM_EVAL_LEDOn(LED10);
+                       memcpy(&context[10+18+1], &wstack, sizeof(wstack));
+                       stk_new->lr = (uint32_t)ret_wrapper_stknoalign_float;
+               } else {
+                       //STM_EVAL_LEDOn(LED9);
+                       memcpy(&context[10+18], &wstack, sizeof(wstack));
+                       stk_new->lr = (uint32_t)ret_wrapper_stkalign_float;
+               }
+
+       }
+
+       /* force return no fpu */
+       context[-4] |= 0x10;
+       /* create exe_return for real state return */
+       context[8] = 0x1337beef;
+       context[8+1] = exe_return;
+}
diff --git a/examples/Makefile b/examples/Makefile
new file mode 100644 (file)
index 0000000..4940940
--- /dev/null
@@ -0,0 +1,49 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+UCGINE ?= $(abspath ..)
+
+CROSS = arm-none-eabi-
+
+ifeq ($(CROSS),arm-none-eabi-)
+subdir-y := spi-flash spi-flash-client test-callout test-cmd test-mk test-uart
+endif
+ifeq ($(CROSS),avr-)
+subdir-y := spi-flash spi-flash-client test-callout test-cmd test-mk test-uart
+endif
+ifeq ($(CROSS),)
+subdir-y := test-cmdline test-mk
+endif
+
+subdir-y := $(dir $(wildcard */Makefile))
+
+include $(UCGINE)/mk/ucgine.mk
+
+.PHONY: all
+all: $(all-targets)
+
+.PHONY: clean
+clean: _ucgine_clean
diff --git a/examples/spi-flash-client/Makefile b/examples/spi-flash-client/Makefile
new file mode 100644 (file)
index 0000000..079eee9
--- /dev/null
@@ -0,0 +1,77 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# XXX
+UCGINE ?= /home/zer0/projects/ucgine
+
+MCU = atmega328p
+F_CPU = 8000000UL
+AVRDUDE_PORT = /dev/ttyUSB0
+AVRDUDE_PROG = arduino
+AVRDUDE_BAUD = 57600
+
+O ?= $(CURDIR)/build
+
+CROSS = avr-
+ifneq ($(CROSS),avr-)
+$(error AVR target only, cannot override CROSS)
+endif
+
+CFLAGS += -g -O2 -Wall
+CFLAGS += -I$(UCGINE)/arch/avr/include
+CFLAGS += -mmcu=$(MCU)
+CFLAGS += -DF_CPU=$(F_CPU)
+LDFLAGS += -mmcu=$(MCU)
+
+# cirbuf
+CFLAGS += -I$(UCGINE)/lib/cirbuf/include
+exe-y-$(O)/spi-client += $(UCGINE)/lib/cirbuf/ucg_cirbuf.c
+# uart
+CFLAGS += -I$(UCGINE)/arch/avr/include
+CFLAGS += -I$(UCGINE)/arch/avr/uart/include
+CFLAGS += -I$(UCGINE)/lib/uart
+CFLAGS += -I$(UCGINE)/lib/uart/include
+exe-y-$(O)/spi-client += $(UCGINE)/lib/uart/ucg_uart.c
+exe-y-$(O)/spi-client += $(UCGINE)/arch/avr/uart/ucg_avr_uart.c
+
+# local files
+exe-y-$(O)/spi-client += main.c uart.c
+
+objcopy-hex-y-$(O)/spi-client.hex := $(O)/spi-client
+objcopy-bin-y-$(O)/spi-client.bin := $(O)/spi-client
+
+include $(UCGINE)/mk/ucgine.mk
+
+.PHONY: all
+all: $(all-targets)
+
+.PHONY: clean
+clean: _ucgine_clean
+
+.PHONY: burn
+burn: all
+         avrdude -e -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROG) \
+               -b $(AVRDUDE_BAUD) -U flash:w:$(O)/spi-client:e
diff --git a/examples/spi-flash-client/main.c b/examples/spi-flash-client/main.c
new file mode 100644 (file)
index 0000000..2fcaeff
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include <ucg_irq.h>
+#include <ucg_delay.h>
+
+#include "uart.h"
+
+static void ss_high(void)
+{
+       printf("ss_high\r\n");
+       PORTB |= (1 << 2);
+}
+
+static void ss_low(void)
+{
+       printf("ss_low\r\n");
+       PORTB &= (~(1 << 2));
+}
+
+static uint8_t spi_sendrecv(uint8_t tx)
+{
+       uint8_t rx;
+
+       SPDR = tx;
+
+       /* Wait for transmission complete */
+       while ((SPSR & (1 << SPIF)) == 0)
+               ;
+
+       ucg_delay_ms(100);
+       rx = SPDR;
+
+       printf("sent 0x%2.2x, recvd 0x%2.2x '%c'\r\n",
+               tx, rx, isprint(rx) ? rx : '.');
+       ucg_delay_ms(1000);
+
+       return rx;
+}
+
+int main(void)
+{
+       uint8_t i;
+
+       /* spi: SS (PB2), MOSI (PB3), SCK (PB5) */
+       DDRB = (1 << 2) | (1 << 3) | (1 << 5);
+
+       ss_high();
+
+       /* blink led before start (unfortunatly it's on SCK, we can't
+        * use it during spi transfer) */
+       for (i = 0; i < 3; i++) {
+               PORTB |= (1 << 5);
+               ucg_delay_ms(500);
+               PORTB &= (~(1 << 5));
+               ucg_delay_ms(500);
+       }
+
+       uart_init();
+
+       ucg_irq_unlock();
+
+       printf("hello\r\n");
+
+#if 0 /* test serial (echo) */
+       {
+               char c;
+               int ret;
+
+               while (1) {
+                       ret = fread(&c, 1, 1, stdin);
+                       if (ret == 1)
+                               fwrite(&c, 1, 1, stdout);
+               }
+       }
+#endif
+
+       /* remove power reduction on spi */
+       PRR &= ~(1 << PRSPI);
+
+       /* Enable SPI, Master, set clock rate fck/16 */
+       SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0);
+
+       ss_low();
+       ucg_delay_ms(1000);
+
+       /* read 5 bytes at address 0x1000 */
+       spi_sendrecv(0x03);
+       spi_sendrecv(0x00);
+       spi_sendrecv(0x10);
+       spi_sendrecv(0x00);
+       for (i = 0; i < 12; i++)
+               spi_sendrecv(0x00); /* data 0 to 12 */
+       ss_high();
+
+       while (1);
+       return 0;
+}
diff --git a/examples/spi-flash-client/uart.c b/examples/spi-flash-client/uart.c
new file mode 100644 (file)
index 0000000..630dc9a
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include <ucg_irq.h>
+#include <ucg_cirbuf.h>
+#include <ucg_uart.h>
+#include <ucg_avr_uart.h>
+
+#include "uart.h"
+
+/* rx & tx buffers */
+static char rx_buf[128];
+static struct ucg_cirbuf rx_cirbuf;
+static char tx_buf[128];
+static struct ucg_cirbuf tx_cirbuf;
+/* generic uart struct */
+static struct ucg_uart main_uart;
+/* avr-specific uart struct */
+static struct ucg_avr_uart avr_uart_data = {
+       .reg_udr = &UDR0,
+       .reg_ucsra = &UCSR0A,
+       .reg_ucsrb = &UCSR0B,
+       .reg_ucsrc = &UCSR0C,
+       .reg_ubrrl = &UBRR0L,
+       .reg_ubrrh = &UBRR0H,
+       .bit_udre = UDRE0,
+       .bit_rxc = RXC0,
+       .bit_udrie = UDRIE0,
+       .bit_rxen = RXEN0,
+       .bit_txen = TXEN0,
+       .bit_rxcie = RXCIE0,
+       .bit_u2x = U2X0,
+};
+
+/* send on stdout */
+static int std_send(char c, FILE *f)
+{
+       (void)f;
+       ucg_uart_send(&main_uart, c, WAIT);
+       return 0;
+}
+
+/* recv on stdin */
+static int std_recv(FILE *f)
+{
+       int16_t c;
+
+       (void)f;
+       c = ucg_uart_recv(&main_uart, NOWAIT);
+       if (c < 0)
+               return _FDEV_EOF;
+
+       return c;
+}
+
+SIGNAL(USART_RX_vect)
+{
+       ucg_uart_rx_intr(&main_uart);
+}
+
+SIGNAL(USART_UDRE_vect)
+{
+       ucg_uart_tx_intr(&main_uart);
+}
+
+int uart_init(void)
+{
+       int ret;
+       struct ucg_uart_config conf;
+
+       ret = ucg_uart_init(&main_uart, &avr_uart_ops, &avr_uart_data,
+               &rx_cirbuf, rx_buf, sizeof(rx_buf),
+               &tx_cirbuf, tx_buf, sizeof(tx_buf));
+       if (ret < 0)
+               return ret;
+       ucg_uart_getconf(&main_uart, &conf);
+       conf.baudrate = 57600;
+       ret = ucg_uart_setconf(&main_uart, &conf);
+       if (ret < 0)
+               return ret;
+
+       fdevopen(std_send, std_recv);
+       return ret;
+}
diff --git a/examples/spi-flash-client/uart.h b/examples/spi-flash-client/uart.h
new file mode 100644 (file)
index 0000000..ca1a139
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UART_H_
+#define UART_H_
+
+#include <stdio.h>
+
+int uart_init(void);
+
+#endif
diff --git a/examples/spi-flash/Makefile b/examples/spi-flash/Makefile
new file mode 100644 (file)
index 0000000..de5be09
--- /dev/null
@@ -0,0 +1,84 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# XXX
+STLINK ?= /home/zer0/projects/stm32/stlink
+UCGINE ?= /home/zer0/projects/ucgine
+STM_COMMON ?= /home/zer0/projects/stm32/stm32_discovery_arm_gcc/STM32F4-Discovery_FW_V1.1.0
+
+O ?= $(CURDIR)/build
+PROG = $(O)/spi-flash
+
+CROSS = arm-none-eabi-
+
+CFLAGS  = -g -O2 -Wall -Tstm32_flash.ld
+CFLAGS += -DUSE_STDPERIPH_DRIVER
+CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
+CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
+CFLAGS += -I.
+CFLAGS += -I$(UCGINE)/arch/stm32/include
+
+# Include files from STM libraries
+CFLAGS += -I$(STM_COMMON)/Utilities/STM32F4-Discovery
+CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/Include
+CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Include
+CFLAGS += -I$(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/inc
+
+CFLAGS += -I$(UCGINE)/lib/gloss/include
+
+LDFLAGS  = -Tstm32_flash.ld --specs=rdimon.specs -lc
+LDFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
+LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
+
+# local files
+exe-y-$(PROG) := main.c uart.c system_stm32f4xx.c
+# stm32 standard periph lib
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_exti.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_gpio.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_rcc.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_spi.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_usart.c
+# startup file
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Source/Templates/TrueSTUDIO/startup_stm32f4xx.s
+# gloss
+exe-y-$(PROG) += $(UCGINE)/lib/gloss/ucg_gloss_stubs.c
+exe-y-$(PROG) += $(UCGINE)/lib/gloss/ucg_gloss_chardev.c
+
+objcopy-hex-y-$(PROG).hex := $(PROG)
+objcopy-bin-y-$(PROG).bin := $(PROG)
+
+include $(UCGINE)/mk/ucgine.mk
+
+.PHONY: all
+all: $(all-targets)
+
+.PHONY: clean
+clean: _ucgine_clean
+
+# Flash the STM32F4
+.PHONY: burn
+burn: all
+       $(STLINK)/st-flash write $(PROG).bin 0x8000000
diff --git a/examples/spi-flash/main.c b/examples/spi-flash/main.c
new file mode 100644 (file)
index 0000000..616eb3b
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ucg_delay.h>
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <stm32f4xx.h>
+
+#include "uart.h"
+
+//#define debug_printf(args...) printf(args)
+#define debug_printf(args...)
+
+struct spi_state {
+       uint8_t cmd;             /* current command */
+#define SPI_F_WREN 0x01          /* write enabled */
+       uint8_t status;          /* status flags */
+       uint32_t len;            /* number of rx/tx bytes since last reset */
+       uint32_t addr;           /* current rd/wr address */
+};
+
+static struct spi_state spi_state;
+
+#define SPI_DATA_LEN 8192        /* must be a power of 2 */
+static uint8_t spi_data[SPI_DATA_LEN];
+
+static void led_on(void)
+{
+       GPIOD->ODR |= (1 << 13);
+}
+
+static void led_off(void)
+{
+       GPIOD->ODR &= (~(1 << 13));
+}
+
+static int spi_init(void)
+{
+       SPI_InitTypeDef spi;
+       GPIO_InitTypeDef gpio;
+
+       /* Enable peripheral clock */
+       RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
+
+       /* Enable the AHB1 peripheral clock for GPIOA. */
+       RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
+       __asm("dsb");
+
+       /* connect the pins to the desired alternate function,
+        * configure them: */
+       GPIO_StructInit(&gpio);
+       gpio.GPIO_Speed = GPIO_Speed_25MHz; /* common */
+       gpio.GPIO_OType = GPIO_OType_PP;
+
+       /* SPI1_NSS: PA4 */
+       gpio.GPIO_Pin = GPIO_Pin_4;
+       gpio.GPIO_Mode = GPIO_Mode_IN; /* do not use the hw NSS */
+       GPIO_Init(GPIOA, &gpio);
+
+       /* SPI1_SCK: PA5 */
+       gpio.GPIO_Pin = GPIO_Pin_5;
+       gpio.GPIO_Mode = GPIO_Mode_AF;
+       GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
+       GPIO_Init(GPIOA, &gpio);
+
+       /* SPI1_MISO: PA6 */
+       gpio.GPIO_Pin = GPIO_Pin_6;
+       gpio.GPIO_Mode = GPIO_Mode_AF;
+       GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
+       GPIO_Init(GPIOA, &gpio);
+
+       /* SPI1_MOSI: PA7 */
+       gpio.GPIO_Pin = GPIO_Pin_7;
+       gpio.GPIO_Mode = GPIO_Mode_AF;
+       GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
+       GPIO_Init(GPIOA, &gpio);
+
+       /* Program the Polarity, Phase, First Data, Baud Rate Prescaler, Slave
+        * Management, Peripheral Mode and CRC Polynomial values */
+       SPI_StructInit(&spi);
+       spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
+       spi.SPI_Mode = SPI_Mode_Slave;
+       spi.SPI_DataSize = SPI_DataSize_8b;
+       spi.SPI_CPOL = SPI_CPOL_Low;   /* CK to 0 when idle */
+       spi.SPI_CPHA = SPI_CPHA_1Edge;  /* data on first transition */
+       spi.SPI_NSS = SPI_NSS_Soft;    /* manage nss by software */
+       spi.SPI_FirstBit = SPI_FirstBit_MSB;
+       SPI_Init(SPI1, &spi);
+
+       /* Enable the SPI */
+       SPI_Cmd(SPI1, ENABLE);
+
+       return 0;
+}
+
+static uint8_t spi_data_read(uint32_t addr)
+{
+       addr &= (SPI_DATA_LEN - 1);
+       return spi_data[addr];
+}
+
+static void spi_data_write(uint32_t addr, uint8_t val)
+{
+       addr &= (SPI_DATA_LEN - 1);
+       spi_data[addr] = val;
+}
+
+/* executed at startup */
+static void spi_state_init(void)
+{
+       memset(&spi_state, 0, sizeof(spi_state));
+}
+
+/* executed between each command when nss goes low */
+static void spi_state_reset(void)
+{
+       SPI_SendData(SPI1, 0);
+       spi_state.cmd = 0;
+       spi_state.len = 0;
+       spi_state.addr = 0;
+}
+
+static void spi_data_intr(void)
+{
+       uint8_t rd_c;
+       uint8_t wr_c = 0;
+
+       rd_c = SPI_ReceiveData(SPI1);
+
+       /* first byte, set command */
+       if (spi_state.len == 0)
+               spi_state.cmd = rd_c;
+
+       switch(spi_state.cmd) {
+       /* Read Memory, no dummy cycle */
+       case 0x03:
+               if (spi_state.len == 0) {
+                       spi_state.addr = 0;
+               } else if (spi_state.len <= 3) {
+                       spi_state.addr <<= 8;
+                       spi_state.addr |= rd_c;
+               }
+               /* send data */
+               if (spi_state.len >= 3) {
+                       wr_c = spi_data_read(spi_state.addr);
+                       spi_state.addr++;
+               }
+               break;
+       /* Read Memory with dummy cycle */
+       case 0x0B:
+               if (spi_state.len == 0) {
+                       spi_state.addr = 0;
+               } else if (spi_state.len <= 3) {
+                       spi_state.addr <<= 8;
+                       spi_state.addr |= rd_c;
+               } else {
+                       wr_c = spi_data_read(spi_state.addr);
+                       spi_state.addr++;
+               }
+               break;
+
+       /* Byte-Program (02H) */
+       case 0x02:
+               if (spi_state.len == 0) {
+                       spi_state.addr = 0;
+               } else if (spi_state.len <= 3) {
+                       spi_state.addr <<= 8;
+                       spi_state.addr |= rd_c;
+               } else if (spi_state.len == 4 &&
+                       (spi_state.status & SPI_F_WREN)) {
+                       spi_data_write(spi_state.addr, rd_c);
+               } else {
+                       /* ignore next bytes */
+               }
+               break;
+
+       /* Write-Enable (06H) */
+       case 0x06:
+               if (spi_state.len == 0)
+                       spi_state.status |= SPI_F_WREN;
+               /* ignore next bytes */
+               break;
+
+       /* Write-Disable (04H) */
+       case 0x04:
+               if (spi_state.len == 0)
+                       spi_state.status &= (~SPI_F_WREN);
+               /* ignore next bytes */
+               break;
+
+       /* Auto Address Increment Programming (ADH) */
+       case 0xAD:
+       /* Erase 4 KByte of memory array (20H) */
+       case 0x20:
+       /* Erase 32 KByte block of memory (52H) */
+       case 0x52:
+       /* Erase 64 KByte block of memory (D8H) */
+       case 0xD8:
+       /* Chip-Erase (60H) or (C7H) */
+       case 0x60:
+       case 0xC7:
+       /* Read-Status-Register (05H) */
+       case 0x05:
+       /* Enable-Write-Status-Register (50H) */
+       case 0x50:
+       /* Write-Status-Register (01H) */
+       case 0x01:
+       /* Read-ID (90H) or (ABH) */
+       case 0x90:
+       case 0xAB:
+       /* JEDEC-ID (9FH) */
+       case 0x9F:
+       /* EnableSOtooutputRY/BY# status during AAI programming (70H) */
+       case 0x70:
+       /* Disable SO as RY/BY# status during AAI programming (80H) */
+       case 0x80:
+
+       default:
+               /* switch on LED on unknown command */
+               led_on();
+               break;
+       }
+
+       SPI_SendData(SPI1, wr_c);
+       spi_state.len++;
+       debug_printf("rx=0x%2.2x tx=0x%2.2x\n", rd_c, wr_c);
+}
+
+/* called on state transition on the NSS pin */
+static void spi_nss_intr(uint8_t nss)
+{
+       if (nss) {
+               debug_printf("ss high\n");
+               SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Set);
+       } else {
+               debug_printf("ss low\n");
+               SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Reset);
+               /* it's a new command, reset our spi_state structure */
+               spi_state_reset();
+       }
+}
+
+int main(void)
+{
+       unsigned i;
+       uint8_t prev_nss = 1, nss;
+
+       /* enable the clock to GPIOD, and stall instruction pipeline as per
+        * errata 2.1.13 "Delay after an RCC peripheral clock enabling" */
+       RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
+       __asm("dsb");
+
+       /* set pin 13 to be general purpose output */
+       GPIOD->MODER = (1 << 26);
+
+       /* toggle the pin */
+       for (i = 0; i < 3; i++) {
+               led_on();
+               ucg_delay_ms(500);
+               led_off();
+               ucg_delay_ms(500);
+       }
+
+       uart_init();
+       uart_register_stdio();
+       printf("salut\n");
+
+       if (spi_init() < 0) {
+               printf("SPI init failed\n");
+               goto end;
+       }
+
+       spi_state_init();
+
+       /* init spi data */
+       memset(spi_data, 0, sizeof(spi_data));
+       strcpy((char *)&spi_data[0x1000], "Dr0idZ C0rp");
+
+       while (1) {
+               /* inspect nss changes */
+               nss = !!(GPIOA->IDR & (1 << 4));
+               if (nss != prev_nss) {
+                       spi_nss_intr(nss);
+                       prev_nss = nss;
+               }
+
+               /* data received */
+               if (SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE) != RESET)
+                       spi_data_intr();
+       }
+
+ end:
+       printf("end\n");
+       while (1);
+       return 0;
+}
diff --git a/examples/spi-flash/stm32_flash.ld b/examples/spi-flash/stm32_flash.ld
new file mode 100755 (executable)
index 0000000..350c05b
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+*****************************************************************************
+**
+**  File        : stm32_flash.ld
+**
+**  Abstract    : Linker script for STM32F407VG Device with
+**                1024KByte FLASH, 192KByte RAM
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used.
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Environment : Atollic TrueSTUDIO(R)
+**
+**  Distribution: The file is distributed \93as is,\94 without any warranty
+**                of any kind.
+**
+**  (c)Copyright Atollic AB.
+**  You may use this file as-is or modify it according to the needs of your
+**  project. Distribution of this file (unmodified or modified) is not
+**  permitted. Atollic AB permit registered Atollic TrueSTUDIO(R) users the
+**  rights to distribute the assembled, compiled & linked contents of this
+**  file as part of an application binary file, provided that it is built
+**  using the Atollic TrueSTUDIO(R) toolchain.
+**
+*****************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Highest address of the user mode stack */
+_estack = 0x20020000;    /* end of 128K RAM on AHB bus*/
+
+/* Generate a link error if heap and stack don't fit into RAM */
+_Min_Heap_Size = 0;      /* required amount of heap  */
+_Min_Stack_Size = 0x400; /* required amount of stack */
+
+/* Specify the memory areas */
+MEMORY
+{
+  FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 1024K
+  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 192K
+  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
+}
+
+/* Define output sections */
+SECTIONS
+{
+  /* The startup code goes first into FLASH */
+  .isr_vector :
+  {
+    . = ALIGN(4);
+    KEEP(*(.isr_vector)) /* Startup code */
+    . = ALIGN(4);
+  } >FLASH
+
+  /* The program code and other data goes into FLASH */
+  .text :
+  {
+    . = ALIGN(4);
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+       *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    . = ALIGN(4);
+    _etext = .;        /* define a global symbols at end of code */
+    _exit = .;
+  } >FLASH
+
+
+   .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
+    .ARM : {
+    __exidx_start = .;
+      *(.ARM.exidx*)
+      __exidx_end = .;
+    } >FLASH
+
+  .preinit_array     :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  } >FLASH
+  .init_array :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } >FLASH
+  .fini_array :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(.fini_array*))
+    KEEP (*(SORT(.fini_array.*)))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } >FLASH
+
+  /* used by the startup to initialize data */
+  _sidata = .;
+
+  /* Initialized data sections goes into RAM, load LMA copy after code */
+  .data : AT ( _sidata )
+  {
+    . = ALIGN(4);
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+
+    . = ALIGN(4);
+    _edata = .;        /* define a global symbol at data end */
+  } >RAM
+
+  /* Uninitialized data section */
+  . = ALIGN(4);
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss secion */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    . = ALIGN(4);
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough RAM left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(4);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    PROVIDE ( __end__ = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(4);
+  } >RAM
+
+  /* MEMORY_bank1 section, code must be located here explicitly            */
+  /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */
+  .memory_b1_text :
+  {
+    *(.mb1text)        /* .mb1text sections (code) */
+    *(.mb1text*)       /* .mb1text* sections (code)  */
+    *(.mb1rodata)      /* read-only data (constants) */
+    *(.mb1rodata*)
+  } >MEMORY_B1
+
+  /* Remove information from the standard libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/examples/spi-flash/stm32f4xx_conf.h b/examples/spi-flash/stm32f4xx_conf.h
new file mode 100644 (file)
index 0000000..74447a8
--- /dev/null
@@ -0,0 +1,94 @@
+/**
+  ******************************************************************************
+  * @file    stm32f4xx_conf.h  
+  * @author  MCD Application Team
+  * @version V1.0.0
+  * @date    19-September-2011
+  * @brief   Library configuration file.
+  ******************************************************************************
+  * @attention
+  *
+  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
+  ******************************************************************************
+  */ 
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32F4xx_CONF_H
+#define __STM32F4xx_CONF_H
+
+#if defined  (HSE_VALUE)
+/* Redefine the HSE value; it's equal to 8 MHz on the STM32F4-DISCOVERY Kit */
+ #undef HSE_VALUE
+ #define HSE_VALUE    ((uint32_t)8000000) 
+#endif /* HSE_VALUE */
+
+/* Includes ------------------------------------------------------------------*/
+/* Uncomment the line below to enable peripheral header file inclusion */
+#include "stm32f4xx_adc.h"
+#include "stm32f4xx_can.h"
+#include "stm32f4xx_crc.h"
+#include "stm32f4xx_cryp.h"
+#include "stm32f4xx_dac.h"
+#include "stm32f4xx_dbgmcu.h"
+#include "stm32f4xx_dcmi.h"
+#include "stm32f4xx_dma.h"
+#include "stm32f4xx_exti.h"
+#include "stm32f4xx_flash.h"
+#include "stm32f4xx_fsmc.h"
+#include "stm32f4xx_hash.h"
+#include "stm32f4xx_gpio.h"
+#include "stm32f4xx_i2c.h"
+#include "stm32f4xx_iwdg.h"
+#include "stm32f4xx_pwr.h"
+#include "stm32f4xx_rcc.h"
+#include "stm32f4xx_rng.h"
+#include "stm32f4xx_rtc.h"
+#include "stm32f4xx_sdio.h"
+#include "stm32f4xx_spi.h"
+#include "stm32f4xx_syscfg.h"
+#include "stm32f4xx_tim.h"
+#include "stm32f4xx_usart.h"
+#include "stm32f4xx_wwdg.h"
+#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/* If an external clock source is used, then the value of the following define 
+   should be set to the value of the external clock source, else, if no external 
+   clock is used, keep this define commented */
+/*#define I2S_EXTERNAL_CLOCK_VAL   12288000 */ /* Value of the external clock in Hz */
+
+
+/* Uncomment the line below to expanse the "assert_param" macro in the 
+   Standard Peripheral Library drivers code */
+/* #define USE_FULL_ASSERT    1 */
+
+/* Exported macro ------------------------------------------------------------*/
+#ifdef  USE_FULL_ASSERT
+
+/**
+  * @brief  The assert_param macro is used for function's parameters check.
+  * @param  expr: If expr is false, it calls assert_failed function
+  *   which reports the name of the source file and the source
+  *   line number of the call that failed. 
+  *   If expr is true, it returns no value.
+  * @retval None
+  */
+  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
+/* Exported functions ------------------------------------------------------- */
+  void assert_failed(uint8_t* file, uint32_t line);
+#else
+  #define assert_param(expr) ((void)0)
+#endif /* USE_FULL_ASSERT */
+
+#endif /* __STM32F4xx_CONF_H */
+
+/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
diff --git a/examples/spi-flash/system_stm32f4xx.c b/examples/spi-flash/system_stm32f4xx.c
new file mode 100644 (file)
index 0000000..98e8e04
--- /dev/null
@@ -0,0 +1,553 @@
+/**
+  ******************************************************************************
+  * @file    system_stm32f4xx.c
+  * @author  MCD Application Team
+  * @version V1.0.0
+  * @date    30-September-2011
+  * @brief   CMSIS Cortex-M4 Device Peripheral Access Layer System Source File.
+  *          This file contains the system clock configuration for STM32F4xx devices,
+  *          and is generated by the clock configuration tool
+  *          stm32f4xx_Clock_Configuration_V1.0.0.xls
+  *             
+  * 1.  This file provides two functions and one global variable to be called from 
+  *     user application:
+  *      - SystemInit(): Setups the system clock (System clock source, PLL Multiplier
+  *                      and Divider factors, AHB/APBx prescalers and Flash settings),
+  *                      depending on the configuration made in the clock xls tool. 
+  *                      This function is called at startup just after reset and 
+  *                      before branch to main program. This call is made inside
+  *                      the "startup_stm32f4xx.s" file.
+  *
+  *      - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
+  *                                  by the user application to setup the SysTick 
+  *                                  timer or configure other parameters.
+  *                                     
+  *      - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
+  *                                 be called whenever the core clock is changed
+  *                                 during program execution.
+  *
+  * 2. After each device reset the HSI (16 MHz) is used as system clock source.
+  *    Then SystemInit() function is called, in "startup_stm32f4xx.s" file, to
+  *    configure the system clock before to branch to main program.
+  *
+  * 3. If the system clock source selected by user fails to startup, the SystemInit()
+  *    function will do nothing and HSI still used as system clock source. User can 
+  *    add some code to deal with this issue inside the SetSysClock() function.
+  *
+  * 4. The default value of HSE crystal is set to 25MHz, refer to "HSE_VALUE" define
+  *    in "stm32f4xx.h" file. When HSE is used as system clock source, directly or
+  *    through PLL, and you are using different crystal you have to adapt the HSE
+  *    value to your own configuration.
+  *
+  * 5. This file configures the system clock as follows:
+  *=============================================================================
+  *=============================================================================
+  *        Supported STM32F4xx device revision    | Rev A
+  *-----------------------------------------------------------------------------
+  *        System Clock source                    | PLL (HSE)
+  *-----------------------------------------------------------------------------
+  *        SYSCLK(Hz)                             | 168000000
+  *-----------------------------------------------------------------------------
+  *        HCLK(Hz)                               | 168000000
+  *-----------------------------------------------------------------------------
+  *        AHB Prescaler                          | 1
+  *-----------------------------------------------------------------------------
+  *        APB1 Prescaler                         | 4
+  *-----------------------------------------------------------------------------
+  *        APB2 Prescaler                         | 2
+  *-----------------------------------------------------------------------------
+  *        HSE Frequency(Hz)                      | 25000000
+  *-----------------------------------------------------------------------------
+  *        PLL_M                                  | 25
+  *-----------------------------------------------------------------------------
+  *        PLL_N                                  | 336
+  *-----------------------------------------------------------------------------
+  *        PLL_P                                  | 2
+  *-----------------------------------------------------------------------------
+  *        PLL_Q                                  | 7
+  *-----------------------------------------------------------------------------
+  *        PLLI2S_N                               | NA
+  *-----------------------------------------------------------------------------
+  *        PLLI2S_R                               | NA
+  *-----------------------------------------------------------------------------
+  *        I2S input clock                        | NA
+  *-----------------------------------------------------------------------------
+  *        VDD(V)                                 | 3.3
+  *-----------------------------------------------------------------------------
+  *        Main regulator output voltage          | Scale1 mode
+  *-----------------------------------------------------------------------------
+  *        Flash Latency(WS)                      | 5
+  *-----------------------------------------------------------------------------
+  *        Prefetch Buffer                        | OFF
+  *-----------------------------------------------------------------------------
+  *        Instruction cache                      | ON
+  *-----------------------------------------------------------------------------
+  *        Data cache                             | ON
+  *-----------------------------------------------------------------------------
+  *        Require 48MHz for USB OTG FS,          | Enabled
+  *        SDIO and RNG clock                     |
+  *-----------------------------------------------------------------------------
+  *=============================================================================
+  ****************************************************************************** 
+  * @attention
+  *
+  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
+  ******************************************************************************
+  */
+
+/** @addtogroup CMSIS
+  * @{
+  */
+
+/** @addtogroup stm32f4xx_system
+  * @{
+  */  
+  
+/** @addtogroup STM32F4xx_System_Private_Includes
+  * @{
+  */
+
+#include "stm32f4xx.h"
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_TypesDefinitions
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Defines
+  * @{
+  */
+
+/************************* Miscellaneous Configuration ************************/
+/*!< Uncomment the following line if you need to use external SRAM mounted
+     on STM324xG_EVAL board as data memory  */
+/* #define DATA_IN_ExtSRAM */
+
+/*!< Uncomment the following line if you need to relocate your vector Table in
+     Internal SRAM. */
+/* #define VECT_TAB_SRAM */
+#define VECT_TAB_OFFSET  0x00 /*!< Vector Table base offset field. 
+                                   This value must be a multiple of 0x200. */
+/******************************************************************************/
+
+/************************* PLL Parameters *************************************/
+/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
+#define PLL_M      25
+#define PLL_N      336
+
+/* SYSCLK = PLL_VCO / PLL_P */
+#define PLL_P      2
+
+/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */
+#define PLL_Q      7
+
+/******************************************************************************/
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Macros
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Variables
+  * @{
+  */
+
+  uint32_t SystemCoreClock = 168000000;
+
+  __I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes
+  * @{
+  */
+
+static void SetSysClock(void);
+#ifdef DATA_IN_ExtSRAM
+  static void SystemInit_ExtMemCtl(void); 
+#endif /* DATA_IN_ExtSRAM */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Functions
+  * @{
+  */
+
+/**
+  * @brief  Setup the microcontroller system
+  *         Initialize the Embedded Flash Interface, the PLL and update the 
+  *         SystemFrequency variable.
+  * @param  None
+  * @retval None
+  */
+void SystemInit(void)
+{
+  /* FPU settings ------------------------------------------------------------*/
+  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
+    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
+  #endif
+
+  /* Reset the RCC clock configuration to the default reset state ------------*/
+  /* Set HSION bit */
+  RCC->CR |= (uint32_t)0x00000001;
+
+  /* Reset CFGR register */
+  RCC->CFGR = 0x00000000;
+
+  /* Reset HSEON, CSSON and PLLON bits */
+  RCC->CR &= (uint32_t)0xFEF6FFFF;
+
+  /* Reset PLLCFGR register */
+  RCC->PLLCFGR = 0x24003010;
+
+  /* Reset HSEBYP bit */
+  RCC->CR &= (uint32_t)0xFFFBFFFF;
+
+  /* Disable all interrupts */
+  RCC->CIR = 0x00000000;
+
+#ifdef DATA_IN_ExtSRAM
+  SystemInit_ExtMemCtl(); 
+#endif /* DATA_IN_ExtSRAM */
+         
+  /* Configure the System clock source, PLL Multiplier and Divider factors, 
+     AHB/APBx prescalers and Flash settings ----------------------------------*/
+  SetSysClock();
+
+  /* Configure the Vector Table location add offset address ------------------*/
+#ifdef VECT_TAB_SRAM
+  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
+#else
+  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
+#endif
+}
+
+/**
+   * @brief  Update SystemCoreClock variable according to Clock Register Values.
+  *         The SystemCoreClock variable contains the core clock (HCLK), it can
+  *         be used by the user application to setup the SysTick timer or configure
+  *         other parameters.
+  *           
+  * @note   Each time the core clock (HCLK) changes, this function must be called
+  *         to update SystemCoreClock variable value. Otherwise, any configuration
+  *         based on this variable will be incorrect.         
+  *     
+  * @note   - The system frequency computed by this function is not the real 
+  *           frequency in the chip. It is calculated based on the predefined 
+  *           constant and the selected clock source:
+  *             
+  *           - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
+  *                                              
+  *           - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
+  *                          
+  *           - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) 
+  *             or HSI_VALUE(*) multiplied/divided by the PLL factors.
+  *         
+  *         (*) HSI_VALUE is a constant defined in stm32f4xx.h file (default value
+  *             16 MHz) but the real value may vary depending on the variations
+  *             in voltage and temperature.   
+  *    
+  *         (**) HSE_VALUE is a constant defined in stm32f4xx.h file (default value
+  *              25 MHz), user has to ensure that HSE_VALUE is same as the real
+  *              frequency of the crystal used. Otherwise, this function may
+  *              have wrong result.
+  *                
+  *         - The result of this function could be not correct when using fractional
+  *           value for HSE crystal.
+  *     
+  * @param  None
+  * @retval None
+  */
+void SystemCoreClockUpdate(void)
+{
+  uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;
+  
+  /* Get SYSCLK source -------------------------------------------------------*/
+  tmp = RCC->CFGR & RCC_CFGR_SWS;
+
+  switch (tmp)
+  {
+    case 0x00:  /* HSI used as system clock source */
+      SystemCoreClock = HSI_VALUE;
+      break;
+    case 0x04:  /* HSE used as system clock source */
+      SystemCoreClock = HSE_VALUE;
+      break;
+    case 0x08:  /* PLL used as system clock source */
+
+      /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
+         SYSCLK = PLL_VCO / PLL_P
+         */    
+      pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
+      pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
+      
+      if (pllsource != 0)
+      {
+        /* HSE used as PLL clock source */
+        pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
+      }
+      else
+      {
+        /* HSI used as PLL clock source */
+        pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);      
+      }
+
+      pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;
+      SystemCoreClock = pllvco/pllp;
+      break;
+    default:
+      SystemCoreClock = HSI_VALUE;
+      break;
+  }
+  /* Compute HCLK frequency --------------------------------------------------*/
+  /* Get HCLK prescaler */
+  tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
+  /* HCLK frequency */
+  SystemCoreClock >>= tmp;
+}
+
+/**
+  * @brief  Configures the System clock source, PLL Multiplier and Divider factors, 
+  *         AHB/APBx prescalers and Flash settings
+  * @Note   This function should be called only once the RCC clock configuration  
+  *         is reset to the default reset state (done in SystemInit() function).   
+  * @param  None
+  * @retval None
+  */
+static void SetSysClock(void)
+{
+/******************************************************************************/
+/*            PLL (clocked by HSE) used as System clock source                */
+/******************************************************************************/
+  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+  
+  /* Enable HSE */
+  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
+  /* Wait till HSE is ready and if Time out is reached exit */
+  do
+  {
+    HSEStatus = RCC->CR & RCC_CR_HSERDY;
+    StartUpCounter++;
+  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
+  {
+    HSEStatus = (uint32_t)0x01;
+  }
+  else
+  {
+    HSEStatus = (uint32_t)0x00;
+  }
+
+  if (HSEStatus == (uint32_t)0x01)
+  {
+    /* Select regulator voltage output Scale 1 mode, System frequency up to 168 MHz */
+    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
+    PWR->CR |= PWR_CR_VOS;
+
+    /* HCLK = SYSCLK / 1*/
+    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
+      
+    /* PCLK2 = HCLK / 2*/
+    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
+    
+    /* PCLK1 = HCLK / 4*/
+    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
+
+    /* Configure the main PLL */
+    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
+                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
+
+    /* Enable the main PLL */
+    RCC->CR |= RCC_CR_PLLON;
+
+    /* Wait till the main PLL is ready */
+    while((RCC->CR & RCC_CR_PLLRDY) == 0)
+    {
+    }
+   
+    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
+    FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
+
+    /* Select the main PLL as system clock source */
+    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
+    RCC->CFGR |= RCC_CFGR_SW_PLL;
+
+    /* Wait till the main PLL is used as system clock source */
+    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
+    {
+    }
+  }
+  else
+  { /* If HSE fails to start-up, the application will have wrong clock
+         configuration. User can add here some code to deal with this error */
+  }
+
+}
+
+/**
+  * @brief  Setup the external memory controller. Called in startup_stm32f4xx.s 
+  *          before jump to __main
+  * @param  None
+  * @retval None
+  */ 
+#ifdef DATA_IN_ExtSRAM
+/**
+  * @brief  Setup the external memory controller.
+  *         Called in startup_stm32f4xx.s before jump to main.
+  *         This function configures the external SRAM mounted on STM324xG_EVAL board
+  *         This SRAM will be used as program data memory (including heap and stack).
+  * @param  None
+  * @retval None
+  */
+void SystemInit_ExtMemCtl(void)
+{
+/*-- GPIOs Configuration -----------------------------------------------------*/
+/*
+ +-------------------+--------------------+------------------+------------------+
+ +                       SRAM pins assignment                                   +
+ +-------------------+--------------------+------------------+------------------+
+ | PD0  <-> FSMC_D2  | PE0  <-> FSMC_NBL0 | PF0  <-> FSMC_A0 | PG0 <-> FSMC_A10 | 
+ | PD1  <-> FSMC_D3  | PE1  <-> FSMC_NBL1 | PF1  <-> FSMC_A1 | PG1 <-> FSMC_A11 | 
+ | PD4  <-> FSMC_NOE | PE3  <-> FSMC_A19  | PF2  <-> FSMC_A2 | PG2 <-> FSMC_A12 | 
+ | PD5  <-> FSMC_NWE | PE4  <-> FSMC_A20  | PF3  <-> FSMC_A3 | PG3 <-> FSMC_A13 | 
+ | PD8  <-> FSMC_D13 | PE7  <-> FSMC_D4   | PF4  <-> FSMC_A4 | PG4 <-> FSMC_A14 | 
+ | PD9  <-> FSMC_D14 | PE8  <-> FSMC_D5   | PF5  <-> FSMC_A5 | PG5 <-> FSMC_A15 | 
+ | PD10 <-> FSMC_D15 | PE9  <-> FSMC_D6   | PF12 <-> FSMC_A6 | PG9 <-> FSMC_NE2 | 
+ | PD11 <-> FSMC_A16 | PE10 <-> FSMC_D7   | PF13 <-> FSMC_A7 |------------------+
+ | PD12 <-> FSMC_A17 | PE11 <-> FSMC_D8   | PF14 <-> FSMC_A8 | 
+ | PD13 <-> FSMC_A18 | PE12 <-> FSMC_D9   | PF15 <-> FSMC_A9 | 
+ | PD14 <-> FSMC_D0  | PE13 <-> FSMC_D10  |------------------+
+ | PD15 <-> FSMC_D1  | PE14 <-> FSMC_D11  |
+ |                   | PE15 <-> FSMC_D12  |
+ +-------------------+--------------------+
+*/
+   /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */
+  RCC->AHB1ENR   = 0x00000078;
+  
+  /* Connect PDx pins to FSMC Alternate function */
+  GPIOD->AFR[0]  = 0x00cc00cc;
+  GPIOD->AFR[1]  = 0xcc0ccccc;
+  /* Configure PDx pins in Alternate function mode */  
+  GPIOD->MODER   = 0xaaaa0a0a;
+  /* Configure PDx pins speed to 100 MHz */  
+  GPIOD->OSPEEDR = 0xffff0f0f;
+  /* Configure PDx pins Output type to push-pull */  
+  GPIOD->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PDx pins */ 
+  GPIOD->PUPDR   = 0x00000000;
+
+  /* Connect PEx pins to FSMC Alternate function */
+  GPIOE->AFR[0]  = 0xc00cc0cc;
+  GPIOE->AFR[1]  = 0xcccccccc;
+  /* Configure PEx pins in Alternate function mode */ 
+  GPIOE->MODER   = 0xaaaa828a;
+  /* Configure PEx pins speed to 100 MHz */ 
+  GPIOE->OSPEEDR = 0xffffc3cf;
+  /* Configure PEx pins Output type to push-pull */  
+  GPIOE->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PEx pins */ 
+  GPIOE->PUPDR   = 0x00000000;
+
+  /* Connect PFx pins to FSMC Alternate function */
+  GPIOF->AFR[0]  = 0x00cccccc;
+  GPIOF->AFR[1]  = 0xcccc0000;
+  /* Configure PFx pins in Alternate function mode */   
+  GPIOF->MODER   = 0xaa000aaa;
+  /* Configure PFx pins speed to 100 MHz */ 
+  GPIOF->OSPEEDR = 0xff000fff;
+  /* Configure PFx pins Output type to push-pull */  
+  GPIOF->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PFx pins */ 
+  GPIOF->PUPDR   = 0x00000000;
+
+  /* Connect PGx pins to FSMC Alternate function */
+  GPIOG->AFR[0]  = 0x00cccccc;
+  GPIOG->AFR[1]  = 0x000000c0;
+  /* Configure PGx pins in Alternate function mode */ 
+  GPIOG->MODER   = 0x00080aaa;
+  /* Configure PGx pins speed to 100 MHz */ 
+  GPIOG->OSPEEDR = 0x000c0fff;
+  /* Configure PGx pins Output type to push-pull */  
+  GPIOG->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PGx pins */ 
+  GPIOG->PUPDR   = 0x00000000;
+  
+/*-- FSMC Configuration ------------------------------------------------------*/
+  /* Enable the FSMC interface clock */
+  RCC->AHB3ENR         = 0x00000001;
+
+  /* Configure and enable Bank1_SRAM2 */
+  FSMC_Bank1->BTCR[2]  = 0x00001015;
+  FSMC_Bank1->BTCR[3]  = 0x00010603;
+  FSMC_Bank1E->BWTR[2] = 0x0fffffff;
+/*
+  Bank1_SRAM2 is configured as follow:
+
+  p.FSMC_AddressSetupTime = 3;
+  p.FSMC_AddressHoldTime = 0;
+  p.FSMC_DataSetupTime = 6;
+  p.FSMC_BusTurnAroundDuration = 1;
+  p.FSMC_CLKDivision = 0;
+  p.FSMC_DataLatency = 0;
+  p.FSMC_AccessMode = FSMC_AccessMode_A;
+
+  FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2;
+  FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_PSRAM;
+  FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
+  FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;  
+  FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
+  FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
+  FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
+  FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;
+  FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;
+*/
+  
+}
+#endif /* DATA_IN_ExtSRAM */
+
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+  
+/**
+  * @}
+  */    
+/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
diff --git a/examples/spi-flash/uart.c b/examples/spi-flash/uart.c
new file mode 100644 (file)
index 0000000..ceb16ea
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include "stm32f4xx.h"
+
+#include <ucg_gloss_chardev.h>
+
+static _ssize_t
+uart_write_r(__attribute__((unused)) struct _reent *r,
+       __attribute__((unused)) int fd,
+       const void *ptr, size_t len)
+{
+       size_t i;
+       const char *buf = ptr;
+
+
+       for (i = 0; i < len; i++) {
+               /* wait that uart is ready */
+               while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET)
+                       ;
+               USART_SendData(USART2, buf[i]);
+       }
+       return len;
+}
+
+static _ssize_t
+uart_read_r(__attribute__((unused)) struct _reent *r,
+       __attribute__((unused)) int fd,
+       void *ptr, size_t len)
+{
+       size_t i;
+       char *buf = ptr;
+
+       for (i = 0; i < len; i++) {
+               /* wait that uart is ready */
+               while (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET)
+                       ;
+               buf[i] = USART_ReceiveData(USART2);
+       }
+       return len;
+}
+
+struct ucg_chardev uart_stdin_dev = {
+       .name = "stdin",
+       .open_r = NULL,
+       .close_r = NULL,
+       .read_r = uart_read_r,
+       .write_r = NULL,
+};
+
+struct ucg_chardev uart_stdout_dev = {
+       .name = "stdout",
+       .open_r = NULL,
+       .close_r = NULL,
+       .read_r = NULL,
+       .write_r = uart_write_r,
+};
+
+struct ucg_chardev uart_stderr_dev = {
+       .name = "stderr",
+       .open_r = NULL,
+       .close_r = NULL,
+       .read_r = NULL,
+       .write_r = uart_write_r,
+};
+
+void uart_init(void)
+{
+       USART_InitTypeDef uart;
+       GPIO_InitTypeDef gpio;
+
+       /* Enable the USART2 peripheral clock. */
+       RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
+       __asm("dsb");
+
+       /* Enable the AHB1 peripheral clock for GPIOA. */
+       RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
+       __asm("dsb");
+
+       /* connect to alternate function */
+       GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
+       GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);
+
+       GPIO_StructInit(&gpio);
+       gpio.GPIO_Speed = GPIO_Speed_25MHz;
+
+       /* configure tx on PA2 */
+       gpio.GPIO_Pin = GPIO_Pin_2;
+       gpio.GPIO_Mode = GPIO_Mode_AF;
+       GPIO_Init(GPIOA, &gpio);
+
+       /* configure rx on PA3 */
+       gpio.GPIO_Pin = GPIO_Pin_3;
+       gpio.GPIO_Mode = GPIO_Mode_AF;
+       GPIO_Init(GPIOA, &gpio);
+
+       /* USART configuration */
+       uart.USART_BaudRate = 115200;
+       uart.USART_WordLength = USART_WordLength_8b;
+       uart.USART_StopBits = USART_StopBits_1;
+       uart.USART_Parity = USART_Parity_No;
+       uart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
+       uart.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
+       USART_Init(USART2, &uart);
+
+       /* Enable USART */
+       USART_Cmd(USART2, ENABLE);
+}
+
+void uart_register_stdio(void)
+{
+       ucg_chardev_register(&uart_stdin_dev);
+       ucg_chardev_register(&uart_stdout_dev);
+       ucg_chardev_register(&uart_stderr_dev);
+
+       /* Disable buffering */
+       setbuf(stdin, NULL);
+       setbuf(stdout, NULL);
+       setbuf(stderr, NULL);
+}
+
diff --git a/examples/spi-flash/uart.h b/examples/spi-flash/uart.h
new file mode 100644 (file)
index 0000000..ac8951a
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UART_H_
+#define UART_H_
+
+/* initialize uart periph */
+void uart_init(void);
+
+/* register standard input/output */
+void uart_register_stdio(void);
+
+#endif /* UART_H_ */
diff --git a/examples/test-callout/Makefile b/examples/test-callout/Makefile
new file mode 100644 (file)
index 0000000..cb136d7
--- /dev/null
@@ -0,0 +1,138 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+UCGINE ?= $(abspath ../..)
+include $(UCGINE)/mk/ucgine-pre.mk
+
+O ?= $(CURDIR)/build
+PROG = $(O)/test-callout
+
+UCGINE_SUBARCH ?= stm32f4
+CFLAGS += -DUCGINE_ARCH_$(UCGINE_ARCH)
+CFLAGS += -DUCGINE_SUBARCH_$(UCGINE_SUBARCH)
+CFLAGS += $(ARCH_CFLAGS)
+CFLAGS += -g -Werror -I.
+
+LDFLAGS += $(ARCH_LDFLAGS)
+
+ifeq ($(UCGINE_ARCH),avr)
+MCU = atmega328p
+F_CPU = 8000000UL
+AVRDUDE_PORT = /dev/ttyUSB0
+AVRDUDE_PROG = arduino
+AVRDUDE_BAUD = 57600
+CFLAGS += -DF_CPU=$(F_CPU)
+CFLAGS += -I$(UCGINE)/arch/avr/uart/include
+CFLAGS += -mmcu=$(MCU)
+LDFLAGS += -mmcu=$(MCU)
+endif
+
+ifeq ($(UCGINE_ARCH),stm32)
+ifeq ($(UCGINE_SUBARCH),stm32f3)
+ver = 3
+STLINK ?= /home/zer0/projects/stm32/stlink
+STM_COMMON ?= /home/zer0/projects/stm32/stm32_discovery_arm_gcc/STM32F3-Discovery_FW_V1.1.0
+CFLAGS += -Tstm32_flash.ld
+CFLAGS += -I$(STM_COMMON)/src
+CFLAGS += -I$(STM_COMMON)/Libraries
+CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/Include
+CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/Device/ST/STM32F30x/Include
+CFLAGS += -I$(STM_COMMON)/Libraries/STM32F30x_StdPeriph_Driver/inc
+CFLAGS += -I$(UCGINE)/lib/gloss/include
+CFLAGS += -I$(UCGINE)/arch/stm32/include
+CFLAGS += -I$(UCGINE)/arch/stm32/uart/include
+LDFLAGS += -Tstm32_flash.ld
+# stm32 standard periph lib
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F30x_StdPeriph_Driver/src/stm32f30x_exti.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F30x_StdPeriph_Driver/src/stm32f30x_gpio.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F30x_StdPeriph_Driver/src/stm32f30x_rcc.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F30x_StdPeriph_Driver/src/stm32f30x_misc.c
+# leds
+exe-y-$(PROG) += $(STM_COMMON)/src/stm32f3_discovery.c
+# system & startup file
+exe-y-$(PROG) += $(STM_COMMON)/src/system_stm32f30x.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/CMSIS/Device/ST/STM32F30x/Source/Templates/TrueSTUDIO/startup_stm32f30x.s
+# reentrant interrupts
+exe-y-$(PROG) += $(UCGINE)/arch/stm32/ucg_reent_intr.c
+else
+ver = 4
+STLINK ?= /home/zer0/projects/stm32/stlink
+STM_COMMON ?= /home/zer0/projects/stm32/stm32_discovery_arm_gcc/STM32F4-Discovery_FW_V1.1.0
+CFLAGS += -Tstm32_flash.ld
+CFLAGS += -I$(STM_COMMON)/Utilities/STM32F4-Discovery
+CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/Include
+CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Include
+CFLAGS += -I$(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/inc
+CFLAGS += -I$(UCGINE)/lib/gloss/include
+CFLAGS += -I$(UCGINE)/arch/stm32/include
+CFLAGS += -I$(UCGINE)/arch/stm32/uart/include
+LDFLAGS += -Tstm32_flash.ld
+exe-y-$(PROG) += system_stm32f4xx.c
+# stm32 standard periph lib
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_exti.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_gpio.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_rcc.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/misc.c
+# leds
+exe-y-$(PROG) += $(STM_COMMON)/Utilities/STM32F4-Discovery/stm32f4_discovery.c
+# startup file
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Source/Templates/TrueSTUDIO/startup_stm32f4xx.s
+# reentrant interrupts
+exe-y-$(PROG) += $(UCGINE)/arch/stm32/ucg_reent_intr.c
+endif
+endif
+
+ifeq ($(UCGINE_ARCH),posix)
+$(error Not supported on posix)
+endif
+
+# callout
+CFLAGS += -I$(UCGINE)/lib/callout/include
+exe-y-$(PROG) += $(UCGINE)/lib/callout/ucg_callout.c
+
+exe-y-$(PROG) += main.c
+
+objcopy-hex-y-$(PROG).hex := $(PROG)
+objcopy-bin-y-$(PROG).bin := $(PROG)
+
+include $(UCGINE)/mk/ucgine-post.mk
+
+.PHONY: all
+all: $(all-targets)
+
+.PHONY: clean
+clean: _ucgine_clean
+
+# Flash the STM32
+.PHONY: burn
+burn: all
+ifeq ($(CROSS),arm-none-eabi-)
+       $(STLINK)/st-flash write $(PROG).bin 0x8000000
+endif
+ifeq ($(CROSS),avr-)
+         avrdude -e -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROG) \
+               -b $(AVRDUDE_BAUD) -U flash:w:$(PROG):e
+endif
diff --git a/examples/test-callout/main.c b/examples/test-callout/main.c
new file mode 100644 (file)
index 0000000..b18654c
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <ucg_delay.h>
+#include <ucg_irq.h>
+#include <ucg_callout.h>
+#include <ucg_reent_intr.h>
+
+static volatile uint32_t global_ms;
+static volatile uint8_t brightness; /* 0 to 100 */
+static volatile int8_t brightness_inc = 1;
+static struct ucg_callout_mgr *p_intr_cm = NULL;
+
+#if defined(UCGINE_ARCH_stm32)
+
+#if defined(UCGINE_SUBARCH_stm32f3)
+#include <stm32f30x.h>
+#include <stm32f3_discovery.h>
+#elif defined(UCGINE_SUBARCH_stm32f4)
+#include <stm32f4xx.h>
+#include <stm32f4_discovery.h>
+#endif
+
+static void target_init(void)
+{
+       RCC_ClocksTypeDef RCC_Clocks;
+
+       /* Get SYSCLK, HCLK and PCLKx frequency */
+       RCC_GetClocksFreq(&RCC_Clocks);
+       /* generate an interrupt every ms */
+       SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000);
+
+       /* Initialize LEDs */
+       STM_EVAL_LEDInit(LED6);
+}
+
+static void led_on(void)
+{
+       STM_EVAL_LEDOn(LED6);
+}
+
+static void led_off(void)
+{
+       STM_EVAL_LEDOff(LED6);
+}
+
+void reent_intr(void)
+{
+       global_ms++;
+       if (p_intr_cm != NULL)
+               ucg_callout_manage(p_intr_cm);
+}
+
+/* called every ms */
+__attribute__ ((naked)) void SysTick_Handler(void)
+{
+       UCG_REENT_INTR(reent_intr);
+}
+#elif defined(UCGINE_ARCH_avr)
+#include <avr/io.h>
+
+static void led_on(void)
+{
+       PORTB |= (1 << 5);
+}
+
+static void led_off(void)
+{
+       PORTB &= (~(1 << 5));
+}
+
+SIGNAL(TIMER0_OVF_vect)
+{
+       static uint16_t i = 0;
+
+       i++;
+       if ((i & 0x3) != 0)
+               return;
+
+       global_ms++;
+       if (p_intr_cm != NULL)
+               ucg_callout_manage(p_intr_cm);
+}
+
+static void target_init(void)
+{
+       DDRB = (1 << 5);
+
+       TCCR0B = (1 << CS01);   /* div = 8 */
+       TIMSK0 |= (1 << TOIE0); /* enable timer0 intr */
+}
+#endif
+
+static uint16_t get_time_ms(void)
+{
+       ucg_irqflags_t flags;
+       uint32_t ms;
+
+       flags = ucg_irq_lock_save();
+       ms = global_ms;
+       ucg_irq_unlock_restore(flags);
+       return ms;
+}
+
+/* every ms, enable or disable the led depending on brightness */
+static void led_control_cb(struct ucg_callout_mgr *cm,
+       struct ucg_callout *tim, void *arg)
+{
+       static uint8_t accu;
+
+       (void)arg;
+
+       accu += brightness;
+       if (accu < 100) {
+               led_off();
+       } else {
+               led_on();
+               accu -= 100;
+       }
+
+       ucg_callout_reschedule(cm, tim, 1);
+}
+
+/* every 50 ms, change increase or decrease the brightness from 0 to 100 */
+static void led_update_brightness_cb(struct ucg_callout_mgr *cm,
+       struct ucg_callout *tim, void *arg)
+{
+       (void)arg;
+
+       brightness += brightness_inc;
+       if (brightness == 0)
+               brightness_inc = 1;
+       else if (brightness == 100)
+               brightness_inc = -1;
+
+       ucg_callout_reschedule(cm, tim, 10);
+}
+
+/* every second, sleep during 100ms */
+static void sleep_cb(struct ucg_callout_mgr *cm,
+       struct ucg_callout *tim, void *arg)
+{
+       (void)arg;
+
+       ucg_delay_ms(200);
+       ucg_callout_reschedule(cm, tim, 300);
+}
+
+int main(void)
+{
+       struct ucg_callout_mgr intr_cm;
+       struct ucg_callout_mgr loop_cm;
+       struct ucg_callout br_timer;
+       struct ucg_callout led_timer;
+       struct ucg_callout sleep_timer;
+       int i;
+
+       target_init();
+
+       /* toggle the pin before starting */
+       for (i = 0; i < 3; i++) {
+               ucg_delay_ms(500);
+               ucg_delay_ms(500);
+       }
+
+       /* init loop callout manager */
+       ucg_callout_mgr_init(&loop_cm, get_time_ms);
+
+       /* load a timer in the loop cmgr that will update the brightness */
+       ucg_callout_init(&br_timer, led_update_brightness_cb, NULL, 128);
+       ucg_callout_schedule(&loop_cm, &br_timer, 0);
+
+       /* init intr callout manager */
+       ucg_callout_mgr_init(&intr_cm, get_time_ms);
+       ucg_irq_lock();
+       p_intr_cm = &intr_cm;
+       ucg_irq_unlock();
+
+       /* load a timer in the intr cmgr that control led on/off */
+       ucg_callout_init(&led_timer, led_control_cb, NULL, 128);
+       ucg_callout_schedule(&intr_cm, &led_timer, 0);
+
+       /* load a lower prio timer in the intr cmgr that just sleeps */
+       ucg_callout_init(&sleep_timer, sleep_cb, NULL, 100);
+       ucg_callout_schedule(&intr_cm, &sleep_timer, 0);
+
+       while (1) {
+               ucg_callout_manage(&loop_cm);
+       }
+
+       while (1);
+       return 0;
+}
diff --git a/examples/test-callout/stm32_flash.ld b/examples/test-callout/stm32_flash.ld
new file mode 100755 (executable)
index 0000000..350c05b
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+*****************************************************************************
+**
+**  File        : stm32_flash.ld
+**
+**  Abstract    : Linker script for STM32F407VG Device with
+**                1024KByte FLASH, 192KByte RAM
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used.
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Environment : Atollic TrueSTUDIO(R)
+**
+**  Distribution: The file is distributed \93as is,\94 without any warranty
+**                of any kind.
+**
+**  (c)Copyright Atollic AB.
+**  You may use this file as-is or modify it according to the needs of your
+**  project. Distribution of this file (unmodified or modified) is not
+**  permitted. Atollic AB permit registered Atollic TrueSTUDIO(R) users the
+**  rights to distribute the assembled, compiled & linked contents of this
+**  file as part of an application binary file, provided that it is built
+**  using the Atollic TrueSTUDIO(R) toolchain.
+**
+*****************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Highest address of the user mode stack */
+_estack = 0x20020000;    /* end of 128K RAM on AHB bus*/
+
+/* Generate a link error if heap and stack don't fit into RAM */
+_Min_Heap_Size = 0;      /* required amount of heap  */
+_Min_Stack_Size = 0x400; /* required amount of stack */
+
+/* Specify the memory areas */
+MEMORY
+{
+  FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 1024K
+  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 192K
+  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
+}
+
+/* Define output sections */
+SECTIONS
+{
+  /* The startup code goes first into FLASH */
+  .isr_vector :
+  {
+    . = ALIGN(4);
+    KEEP(*(.isr_vector)) /* Startup code */
+    . = ALIGN(4);
+  } >FLASH
+
+  /* The program code and other data goes into FLASH */
+  .text :
+  {
+    . = ALIGN(4);
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+       *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    . = ALIGN(4);
+    _etext = .;        /* define a global symbols at end of code */
+    _exit = .;
+  } >FLASH
+
+
+   .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
+    .ARM : {
+    __exidx_start = .;
+      *(.ARM.exidx*)
+      __exidx_end = .;
+    } >FLASH
+
+  .preinit_array     :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  } >FLASH
+  .init_array :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } >FLASH
+  .fini_array :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(.fini_array*))
+    KEEP (*(SORT(.fini_array.*)))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } >FLASH
+
+  /* used by the startup to initialize data */
+  _sidata = .;
+
+  /* Initialized data sections goes into RAM, load LMA copy after code */
+  .data : AT ( _sidata )
+  {
+    . = ALIGN(4);
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+
+    . = ALIGN(4);
+    _edata = .;        /* define a global symbol at data end */
+  } >RAM
+
+  /* Uninitialized data section */
+  . = ALIGN(4);
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss secion */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    . = ALIGN(4);
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough RAM left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(4);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    PROVIDE ( __end__ = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(4);
+  } >RAM
+
+  /* MEMORY_bank1 section, code must be located here explicitly            */
+  /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */
+  .memory_b1_text :
+  {
+    *(.mb1text)        /* .mb1text sections (code) */
+    *(.mb1text*)       /* .mb1text* sections (code)  */
+    *(.mb1rodata)      /* read-only data (constants) */
+    *(.mb1rodata*)
+  } >MEMORY_B1
+
+  /* Remove information from the standard libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/examples/test-callout/stm32f4xx_conf.h b/examples/test-callout/stm32f4xx_conf.h
new file mode 100644 (file)
index 0000000..74447a8
--- /dev/null
@@ -0,0 +1,94 @@
+/**
+  ******************************************************************************
+  * @file    stm32f4xx_conf.h  
+  * @author  MCD Application Team
+  * @version V1.0.0
+  * @date    19-September-2011
+  * @brief   Library configuration file.
+  ******************************************************************************
+  * @attention
+  *
+  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
+  ******************************************************************************
+  */ 
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32F4xx_CONF_H
+#define __STM32F4xx_CONF_H
+
+#if defined  (HSE_VALUE)
+/* Redefine the HSE value; it's equal to 8 MHz on the STM32F4-DISCOVERY Kit */
+ #undef HSE_VALUE
+ #define HSE_VALUE    ((uint32_t)8000000) 
+#endif /* HSE_VALUE */
+
+/* Includes ------------------------------------------------------------------*/
+/* Uncomment the line below to enable peripheral header file inclusion */
+#include "stm32f4xx_adc.h"
+#include "stm32f4xx_can.h"
+#include "stm32f4xx_crc.h"
+#include "stm32f4xx_cryp.h"
+#include "stm32f4xx_dac.h"
+#include "stm32f4xx_dbgmcu.h"
+#include "stm32f4xx_dcmi.h"
+#include "stm32f4xx_dma.h"
+#include "stm32f4xx_exti.h"
+#include "stm32f4xx_flash.h"
+#include "stm32f4xx_fsmc.h"
+#include "stm32f4xx_hash.h"
+#include "stm32f4xx_gpio.h"
+#include "stm32f4xx_i2c.h"
+#include "stm32f4xx_iwdg.h"
+#include "stm32f4xx_pwr.h"
+#include "stm32f4xx_rcc.h"
+#include "stm32f4xx_rng.h"
+#include "stm32f4xx_rtc.h"
+#include "stm32f4xx_sdio.h"
+#include "stm32f4xx_spi.h"
+#include "stm32f4xx_syscfg.h"
+#include "stm32f4xx_tim.h"
+#include "stm32f4xx_usart.h"
+#include "stm32f4xx_wwdg.h"
+#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/* If an external clock source is used, then the value of the following define 
+   should be set to the value of the external clock source, else, if no external 
+   clock is used, keep this define commented */
+/*#define I2S_EXTERNAL_CLOCK_VAL   12288000 */ /* Value of the external clock in Hz */
+
+
+/* Uncomment the line below to expanse the "assert_param" macro in the 
+   Standard Peripheral Library drivers code */
+/* #define USE_FULL_ASSERT    1 */
+
+/* Exported macro ------------------------------------------------------------*/
+#ifdef  USE_FULL_ASSERT
+
+/**
+  * @brief  The assert_param macro is used for function's parameters check.
+  * @param  expr: If expr is false, it calls assert_failed function
+  *   which reports the name of the source file and the source
+  *   line number of the call that failed. 
+  *   If expr is true, it returns no value.
+  * @retval None
+  */
+  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
+/* Exported functions ------------------------------------------------------- */
+  void assert_failed(uint8_t* file, uint32_t line);
+#else
+  #define assert_param(expr) ((void)0)
+#endif /* USE_FULL_ASSERT */
+
+#endif /* __STM32F4xx_CONF_H */
+
+/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
diff --git a/examples/test-callout/system_stm32f4xx.c b/examples/test-callout/system_stm32f4xx.c
new file mode 100644 (file)
index 0000000..98e8e04
--- /dev/null
@@ -0,0 +1,553 @@
+/**
+  ******************************************************************************
+  * @file    system_stm32f4xx.c
+  * @author  MCD Application Team
+  * @version V1.0.0
+  * @date    30-September-2011
+  * @brief   CMSIS Cortex-M4 Device Peripheral Access Layer System Source File.
+  *          This file contains the system clock configuration for STM32F4xx devices,
+  *          and is generated by the clock configuration tool
+  *          stm32f4xx_Clock_Configuration_V1.0.0.xls
+  *             
+  * 1.  This file provides two functions and one global variable to be called from 
+  *     user application:
+  *      - SystemInit(): Setups the system clock (System clock source, PLL Multiplier
+  *                      and Divider factors, AHB/APBx prescalers and Flash settings),
+  *                      depending on the configuration made in the clock xls tool. 
+  *                      This function is called at startup just after reset and 
+  *                      before branch to main program. This call is made inside
+  *                      the "startup_stm32f4xx.s" file.
+  *
+  *      - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
+  *                                  by the user application to setup the SysTick 
+  *                                  timer or configure other parameters.
+  *                                     
+  *      - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
+  *                                 be called whenever the core clock is changed
+  *                                 during program execution.
+  *
+  * 2. After each device reset the HSI (16 MHz) is used as system clock source.
+  *    Then SystemInit() function is called, in "startup_stm32f4xx.s" file, to
+  *    configure the system clock before to branch to main program.
+  *
+  * 3. If the system clock source selected by user fails to startup, the SystemInit()
+  *    function will do nothing and HSI still used as system clock source. User can 
+  *    add some code to deal with this issue inside the SetSysClock() function.
+  *
+  * 4. The default value of HSE crystal is set to 25MHz, refer to "HSE_VALUE" define
+  *    in "stm32f4xx.h" file. When HSE is used as system clock source, directly or
+  *    through PLL, and you are using different crystal you have to adapt the HSE
+  *    value to your own configuration.
+  *
+  * 5. This file configures the system clock as follows:
+  *=============================================================================
+  *=============================================================================
+  *        Supported STM32F4xx device revision    | Rev A
+  *-----------------------------------------------------------------------------
+  *        System Clock source                    | PLL (HSE)
+  *-----------------------------------------------------------------------------
+  *        SYSCLK(Hz)                             | 168000000
+  *-----------------------------------------------------------------------------
+  *        HCLK(Hz)                               | 168000000
+  *-----------------------------------------------------------------------------
+  *        AHB Prescaler                          | 1
+  *-----------------------------------------------------------------------------
+  *        APB1 Prescaler                         | 4
+  *-----------------------------------------------------------------------------
+  *        APB2 Prescaler                         | 2
+  *-----------------------------------------------------------------------------
+  *        HSE Frequency(Hz)                      | 25000000
+  *-----------------------------------------------------------------------------
+  *        PLL_M                                  | 25
+  *-----------------------------------------------------------------------------
+  *        PLL_N                                  | 336
+  *-----------------------------------------------------------------------------
+  *        PLL_P                                  | 2
+  *-----------------------------------------------------------------------------
+  *        PLL_Q                                  | 7
+  *-----------------------------------------------------------------------------
+  *        PLLI2S_N                               | NA
+  *-----------------------------------------------------------------------------
+  *        PLLI2S_R                               | NA
+  *-----------------------------------------------------------------------------
+  *        I2S input clock                        | NA
+  *-----------------------------------------------------------------------------
+  *        VDD(V)                                 | 3.3
+  *-----------------------------------------------------------------------------
+  *        Main regulator output voltage          | Scale1 mode
+  *-----------------------------------------------------------------------------
+  *        Flash Latency(WS)                      | 5
+  *-----------------------------------------------------------------------------
+  *        Prefetch Buffer                        | OFF
+  *-----------------------------------------------------------------------------
+  *        Instruction cache                      | ON
+  *-----------------------------------------------------------------------------
+  *        Data cache                             | ON
+  *-----------------------------------------------------------------------------
+  *        Require 48MHz for USB OTG FS,          | Enabled
+  *        SDIO and RNG clock                     |
+  *-----------------------------------------------------------------------------
+  *=============================================================================
+  ****************************************************************************** 
+  * @attention
+  *
+  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
+  ******************************************************************************
+  */
+
+/** @addtogroup CMSIS
+  * @{
+  */
+
+/** @addtogroup stm32f4xx_system
+  * @{
+  */  
+  
+/** @addtogroup STM32F4xx_System_Private_Includes
+  * @{
+  */
+
+#include "stm32f4xx.h"
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_TypesDefinitions
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Defines
+  * @{
+  */
+
+/************************* Miscellaneous Configuration ************************/
+/*!< Uncomment the following line if you need to use external SRAM mounted
+     on STM324xG_EVAL board as data memory  */
+/* #define DATA_IN_ExtSRAM */
+
+/*!< Uncomment the following line if you need to relocate your vector Table in
+     Internal SRAM. */
+/* #define VECT_TAB_SRAM */
+#define VECT_TAB_OFFSET  0x00 /*!< Vector Table base offset field. 
+                                   This value must be a multiple of 0x200. */
+/******************************************************************************/
+
+/************************* PLL Parameters *************************************/
+/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
+#define PLL_M      25
+#define PLL_N      336
+
+/* SYSCLK = PLL_VCO / PLL_P */
+#define PLL_P      2
+
+/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */
+#define PLL_Q      7
+
+/******************************************************************************/
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Macros
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Variables
+  * @{
+  */
+
+  uint32_t SystemCoreClock = 168000000;
+
+  __I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes
+  * @{
+  */
+
+static void SetSysClock(void);
+#ifdef DATA_IN_ExtSRAM
+  static void SystemInit_ExtMemCtl(void); 
+#endif /* DATA_IN_ExtSRAM */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Functions
+  * @{
+  */
+
+/**
+  * @brief  Setup the microcontroller system
+  *         Initialize the Embedded Flash Interface, the PLL and update the 
+  *         SystemFrequency variable.
+  * @param  None
+  * @retval None
+  */
+void SystemInit(void)
+{
+  /* FPU settings ------------------------------------------------------------*/
+  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
+    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
+  #endif
+
+  /* Reset the RCC clock configuration to the default reset state ------------*/
+  /* Set HSION bit */
+  RCC->CR |= (uint32_t)0x00000001;
+
+  /* Reset CFGR register */
+  RCC->CFGR = 0x00000000;
+
+  /* Reset HSEON, CSSON and PLLON bits */
+  RCC->CR &= (uint32_t)0xFEF6FFFF;
+
+  /* Reset PLLCFGR register */
+  RCC->PLLCFGR = 0x24003010;
+
+  /* Reset HSEBYP bit */
+  RCC->CR &= (uint32_t)0xFFFBFFFF;
+
+  /* Disable all interrupts */
+  RCC->CIR = 0x00000000;
+
+#ifdef DATA_IN_ExtSRAM
+  SystemInit_ExtMemCtl(); 
+#endif /* DATA_IN_ExtSRAM */
+         
+  /* Configure the System clock source, PLL Multiplier and Divider factors, 
+     AHB/APBx prescalers and Flash settings ----------------------------------*/
+  SetSysClock();
+
+  /* Configure the Vector Table location add offset address ------------------*/
+#ifdef VECT_TAB_SRAM
+  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
+#else
+  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
+#endif
+}
+
+/**
+   * @brief  Update SystemCoreClock variable according to Clock Register Values.
+  *         The SystemCoreClock variable contains the core clock (HCLK), it can
+  *         be used by the user application to setup the SysTick timer or configure
+  *         other parameters.
+  *           
+  * @note   Each time the core clock (HCLK) changes, this function must be called
+  *         to update SystemCoreClock variable value. Otherwise, any configuration
+  *         based on this variable will be incorrect.         
+  *     
+  * @note   - The system frequency computed by this function is not the real 
+  *           frequency in the chip. It is calculated based on the predefined 
+  *           constant and the selected clock source:
+  *             
+  *           - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
+  *                                              
+  *           - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
+  *                          
+  *           - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) 
+  *             or HSI_VALUE(*) multiplied/divided by the PLL factors.
+  *         
+  *         (*) HSI_VALUE is a constant defined in stm32f4xx.h file (default value
+  *             16 MHz) but the real value may vary depending on the variations
+  *             in voltage and temperature.   
+  *    
+  *         (**) HSE_VALUE is a constant defined in stm32f4xx.h file (default value
+  *              25 MHz), user has to ensure that HSE_VALUE is same as the real
+  *              frequency of the crystal used. Otherwise, this function may
+  *              have wrong result.
+  *                
+  *         - The result of this function could be not correct when using fractional
+  *           value for HSE crystal.
+  *     
+  * @param  None
+  * @retval None
+  */
+void SystemCoreClockUpdate(void)
+{
+  uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;
+  
+  /* Get SYSCLK source -------------------------------------------------------*/
+  tmp = RCC->CFGR & RCC_CFGR_SWS;
+
+  switch (tmp)
+  {
+    case 0x00:  /* HSI used as system clock source */
+      SystemCoreClock = HSI_VALUE;
+      break;
+    case 0x04:  /* HSE used as system clock source */
+      SystemCoreClock = HSE_VALUE;
+      break;
+    case 0x08:  /* PLL used as system clock source */
+
+      /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
+         SYSCLK = PLL_VCO / PLL_P
+         */    
+      pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
+      pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
+      
+      if (pllsource != 0)
+      {
+        /* HSE used as PLL clock source */
+        pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
+      }
+      else
+      {
+        /* HSI used as PLL clock source */
+        pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);      
+      }
+
+      pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;
+      SystemCoreClock = pllvco/pllp;
+      break;
+    default:
+      SystemCoreClock = HSI_VALUE;
+      break;
+  }
+  /* Compute HCLK frequency --------------------------------------------------*/
+  /* Get HCLK prescaler */
+  tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
+  /* HCLK frequency */
+  SystemCoreClock >>= tmp;
+}
+
+/**
+  * @brief  Configures the System clock source, PLL Multiplier and Divider factors, 
+  *         AHB/APBx prescalers and Flash settings
+  * @Note   This function should be called only once the RCC clock configuration  
+  *         is reset to the default reset state (done in SystemInit() function).   
+  * @param  None
+  * @retval None
+  */
+static void SetSysClock(void)
+{
+/******************************************************************************/
+/*            PLL (clocked by HSE) used as System clock source                */
+/******************************************************************************/
+  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+  
+  /* Enable HSE */
+  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
+  /* Wait till HSE is ready and if Time out is reached exit */
+  do
+  {
+    HSEStatus = RCC->CR & RCC_CR_HSERDY;
+    StartUpCounter++;
+  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
+  {
+    HSEStatus = (uint32_t)0x01;
+  }
+  else
+  {
+    HSEStatus = (uint32_t)0x00;
+  }
+
+  if (HSEStatus == (uint32_t)0x01)
+  {
+    /* Select regulator voltage output Scale 1 mode, System frequency up to 168 MHz */
+    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
+    PWR->CR |= PWR_CR_VOS;
+
+    /* HCLK = SYSCLK / 1*/
+    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
+      
+    /* PCLK2 = HCLK / 2*/
+    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
+    
+    /* PCLK1 = HCLK / 4*/
+    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
+
+    /* Configure the main PLL */
+    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
+                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
+
+    /* Enable the main PLL */
+    RCC->CR |= RCC_CR_PLLON;
+
+    /* Wait till the main PLL is ready */
+    while((RCC->CR & RCC_CR_PLLRDY) == 0)
+    {
+    }
+   
+    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
+    FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
+
+    /* Select the main PLL as system clock source */
+    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
+    RCC->CFGR |= RCC_CFGR_SW_PLL;
+
+    /* Wait till the main PLL is used as system clock source */
+    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
+    {
+    }
+  }
+  else
+  { /* If HSE fails to start-up, the application will have wrong clock
+         configuration. User can add here some code to deal with this error */
+  }
+
+}
+
+/**
+  * @brief  Setup the external memory controller. Called in startup_stm32f4xx.s 
+  *          before jump to __main
+  * @param  None
+  * @retval None
+  */ 
+#ifdef DATA_IN_ExtSRAM
+/**
+  * @brief  Setup the external memory controller.
+  *         Called in startup_stm32f4xx.s before jump to main.
+  *         This function configures the external SRAM mounted on STM324xG_EVAL board
+  *         This SRAM will be used as program data memory (including heap and stack).
+  * @param  None
+  * @retval None
+  */
+void SystemInit_ExtMemCtl(void)
+{
+/*-- GPIOs Configuration -----------------------------------------------------*/
+/*
+ +-------------------+--------------------+------------------+------------------+
+ +                       SRAM pins assignment                                   +
+ +-------------------+--------------------+------------------+------------------+
+ | PD0  <-> FSMC_D2  | PE0  <-> FSMC_NBL0 | PF0  <-> FSMC_A0 | PG0 <-> FSMC_A10 | 
+ | PD1  <-> FSMC_D3  | PE1  <-> FSMC_NBL1 | PF1  <-> FSMC_A1 | PG1 <-> FSMC_A11 | 
+ | PD4  <-> FSMC_NOE | PE3  <-> FSMC_A19  | PF2  <-> FSMC_A2 | PG2 <-> FSMC_A12 | 
+ | PD5  <-> FSMC_NWE | PE4  <-> FSMC_A20  | PF3  <-> FSMC_A3 | PG3 <-> FSMC_A13 | 
+ | PD8  <-> FSMC_D13 | PE7  <-> FSMC_D4   | PF4  <-> FSMC_A4 | PG4 <-> FSMC_A14 | 
+ | PD9  <-> FSMC_D14 | PE8  <-> FSMC_D5   | PF5  <-> FSMC_A5 | PG5 <-> FSMC_A15 | 
+ | PD10 <-> FSMC_D15 | PE9  <-> FSMC_D6   | PF12 <-> FSMC_A6 | PG9 <-> FSMC_NE2 | 
+ | PD11 <-> FSMC_A16 | PE10 <-> FSMC_D7   | PF13 <-> FSMC_A7 |------------------+
+ | PD12 <-> FSMC_A17 | PE11 <-> FSMC_D8   | PF14 <-> FSMC_A8 | 
+ | PD13 <-> FSMC_A18 | PE12 <-> FSMC_D9   | PF15 <-> FSMC_A9 | 
+ | PD14 <-> FSMC_D0  | PE13 <-> FSMC_D10  |------------------+
+ | PD15 <-> FSMC_D1  | PE14 <-> FSMC_D11  |
+ |                   | PE15 <-> FSMC_D12  |
+ +-------------------+--------------------+
+*/
+   /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */
+  RCC->AHB1ENR   = 0x00000078;
+  
+  /* Connect PDx pins to FSMC Alternate function */
+  GPIOD->AFR[0]  = 0x00cc00cc;
+  GPIOD->AFR[1]  = 0xcc0ccccc;
+  /* Configure PDx pins in Alternate function mode */  
+  GPIOD->MODER   = 0xaaaa0a0a;
+  /* Configure PDx pins speed to 100 MHz */  
+  GPIOD->OSPEEDR = 0xffff0f0f;
+  /* Configure PDx pins Output type to push-pull */  
+  GPIOD->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PDx pins */ 
+  GPIOD->PUPDR   = 0x00000000;
+
+  /* Connect PEx pins to FSMC Alternate function */
+  GPIOE->AFR[0]  = 0xc00cc0cc;
+  GPIOE->AFR[1]  = 0xcccccccc;
+  /* Configure PEx pins in Alternate function mode */ 
+  GPIOE->MODER   = 0xaaaa828a;
+  /* Configure PEx pins speed to 100 MHz */ 
+  GPIOE->OSPEEDR = 0xffffc3cf;
+  /* Configure PEx pins Output type to push-pull */  
+  GPIOE->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PEx pins */ 
+  GPIOE->PUPDR   = 0x00000000;
+
+  /* Connect PFx pins to FSMC Alternate function */
+  GPIOF->AFR[0]  = 0x00cccccc;
+  GPIOF->AFR[1]  = 0xcccc0000;
+  /* Configure PFx pins in Alternate function mode */   
+  GPIOF->MODER   = 0xaa000aaa;
+  /* Configure PFx pins speed to 100 MHz */ 
+  GPIOF->OSPEEDR = 0xff000fff;
+  /* Configure PFx pins Output type to push-pull */  
+  GPIOF->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PFx pins */ 
+  GPIOF->PUPDR   = 0x00000000;
+
+  /* Connect PGx pins to FSMC Alternate function */
+  GPIOG->AFR[0]  = 0x00cccccc;
+  GPIOG->AFR[1]  = 0x000000c0;
+  /* Configure PGx pins in Alternate function mode */ 
+  GPIOG->MODER   = 0x00080aaa;
+  /* Configure PGx pins speed to 100 MHz */ 
+  GPIOG->OSPEEDR = 0x000c0fff;
+  /* Configure PGx pins Output type to push-pull */  
+  GPIOG->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PGx pins */ 
+  GPIOG->PUPDR   = 0x00000000;
+  
+/*-- FSMC Configuration ------------------------------------------------------*/
+  /* Enable the FSMC interface clock */
+  RCC->AHB3ENR         = 0x00000001;
+
+  /* Configure and enable Bank1_SRAM2 */
+  FSMC_Bank1->BTCR[2]  = 0x00001015;
+  FSMC_Bank1->BTCR[3]  = 0x00010603;
+  FSMC_Bank1E->BWTR[2] = 0x0fffffff;
+/*
+  Bank1_SRAM2 is configured as follow:
+
+  p.FSMC_AddressSetupTime = 3;
+  p.FSMC_AddressHoldTime = 0;
+  p.FSMC_DataSetupTime = 6;
+  p.FSMC_BusTurnAroundDuration = 1;
+  p.FSMC_CLKDivision = 0;
+  p.FSMC_DataLatency = 0;
+  p.FSMC_AccessMode = FSMC_AccessMode_A;
+
+  FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2;
+  FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_PSRAM;
+  FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
+  FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;  
+  FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
+  FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
+  FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
+  FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;
+  FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;
+*/
+  
+}
+#endif /* DATA_IN_ExtSRAM */
+
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+  
+/**
+  * @}
+  */    
+/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
diff --git a/examples/test-cmd/Makefile b/examples/test-cmd/Makefile
new file mode 100644 (file)
index 0000000..6e63c88
--- /dev/null
@@ -0,0 +1,129 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+UCGINE ?= $(abspath ../..)
+include $(UCGINE)/mk/ucgine-pre.mk
+
+O ?= $(CURDIR)/build
+PROG = $(O)/test-cmd
+
+CFLAGS += $(ARCH_CFLAGS)
+CFLAGS += -g -Werror -I.
+
+LDFLAGS += $(ARCH_LDFLAGS)
+
+# local files
+exe-y-$(PROG) := main.c commands.c uart.c
+
+ifeq ($(UCGINE_ARCH),avr)
+MCU = atmega328p
+F_CPU = 8000000UL
+AVRDUDE_PORT = /dev/ttyUSB0
+AVRDUDE_PROG = arduino
+AVRDUDE_BAUD = 57600
+CFLAGS += -DF_CPU=$(F_CPU)
+CFLAGS += -I$(UCGINE)/arch/avr/uart/include
+CFLAGS += -mmcu=$(MCU)
+CFLAGS += -DUCG_CMD_NO_PAGER
+LDFLAGS += -mmcu=$(MCU)
+# uart
+exe-y-$(PROG) += $(UCGINE)/arch/avr/uart/ucg_avr_uart.c
+endif
+ifeq ($(UCGINE_ARCH),stm32)
+STLINK ?= /home/zer0/projects/stm32/stlink
+STM_COMMON ?= /home/zer0/projects/stm32/stm32_discovery_arm_gcc/STM32F4-Discovery_FW_V1.1.0
+CFLAGS += -Tstm32_flash.ld
+CFLAGS += -I$(STM_COMMON)/Utilities/STM32F4-Discovery
+CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/Include
+CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Include
+CFLAGS += -I$(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/inc
+CFLAGS += -I$(UCGINE)/lib/gloss/include
+CFLAGS += -I$(UCGINE)/arch/stm32/include
+CFLAGS += -I$(UCGINE)/arch/stm32/uart/include
+LDFLAGS += -Tstm32_flash.ld
+exe-y-$(PROG) += system_stm32f4xx.c
+# stm32 standard periph lib
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_exti.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_gpio.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_rcc.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_usart.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/misc.c
+# startup file
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Source/Templates/TrueSTUDIO/startup_stm32f4xx.s
+# gloss
+exe-y-$(PROG) += $(UCGINE)/lib/gloss/ucg_gloss_stubs.c
+exe-y-$(PROG) += $(UCGINE)/lib/gloss/ucg_gloss_chardev.c
+# uart
+exe-y-$(PROG) += $(UCGINE)/arch/stm32/uart/ucg_stm32_uart.c
+endif
+ifeq ($(UCGINE_ARCH),posix)
+CFLAGS += -DUCG_CMD_HAVE_SOCKET -DUCG_CMD_HAVE_TERMIOS
+endif
+
+# cirbuf
+CFLAGS += -I$(UCGINE)/lib/cirbuf/include
+exe-y-$(PROG) += $(UCGINE)/lib/cirbuf/ucg_cirbuf.c
+# uart
+CFLAGS += -I$(UCGINE)/lib/uart/include
+exe-y-$(PROG) += $(UCGINE)/lib/uart/ucg_uart.c
+# cmd
+CFLAGS += -I$(UCGINE)/lib/cmd/include
+exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd.c
+exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_parse.c
+ifeq ($(CROSS),)
+exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_parse_etheraddr.c
+exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_parse_file.c
+exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_parse_ipaddr.c
+exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_socket.c
+endif
+exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_termios.c
+exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_parse_num.c
+exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_parse_string.c
+exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_rdline.c
+exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_vt100.c
+
+objcopy-hex-y-$(PROG).hex := $(PROG)
+objcopy-bin-y-$(PROG).bin := $(PROG)
+
+# XXX where to define the all target?
+include $(UCGINE)/mk/ucgine-post.mk
+
+.PHONY: all
+all: $(all-targets)
+       $(CROSS)size $(PROG)
+
+.PHONY: clean
+clean: _ucgine_clean
+
+.PHONY: burn
+burn: all
+ifeq ($(UCGINE_ARCH),stm32)
+       $(STLINK)/st-flash write $(PROG).bin 0x8000000
+endif
+ifeq ($(UCGINE_ARCH),avr)
+         avrdude -e -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROG) \
+               -b $(AVRDUDE_BAUD) -U flash:w:$(PROG):e
+endif
diff --git a/examples/test-cmd/commands.c b/examples/test-cmd/commands.c
new file mode 100644 (file)
index 0000000..9b24303
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <ucg_cmd_parse.h>
+#include <ucg_cmd_parse_num.h>
+#include <ucg_cmd_parse_string.h>
+#include <ucg_cmd.h>
+
+/**********************************************************/
+
+struct cmd_hello_result {
+       ucg_cmd_fixed_string_t hello;
+       ucg_cmd_fixed_string_t name;
+};
+
+static void cmd_hello_parsed(void *parsed_result,
+       struct ucg_cmd *cl, void *data)
+{
+       struct cmd_hello_result *res = parsed_result;
+
+       (void)data;
+       ucg_cmd_printf(cl, "hello %s\n", res->name);
+}
+
+static ucg_cmd_tk_string_t cmd_hello_hello =
+       UCG_CMD_TK_STRING(struct cmd_hello_result, hello, "hello");
+
+static ucg_cmd_tk_string_t cmd_hello_name =
+       UCG_CMD_TK_STRING(struct cmd_hello_result, name, NULL);
+
+static ucg_cmd_inst_t cmd_hello = {
+       .f = cmd_hello_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "Say hello",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_hello_hello,
+               (void *)&cmd_hello_name,
+               NULL,
+       },
+};
+
+/**********************************************************/
+
+struct cmd_hello_tutu_result {
+       ucg_cmd_fixed_string_t hello;
+       ucg_cmd_fixed_string_t name1;
+       ucg_cmd_fixed_string_t name2;
+};
+
+static void cmd_hello_tutu_parsed(void *parsed_result,
+       struct ucg_cmd *cl, void *data)
+{
+       struct cmd_hello_tutu_result *res = parsed_result;
+
+       (void)data;
+       ucg_cmd_printf(cl, "hello %s and %s\n", res->name1, res->name2);
+}
+
+static ucg_cmd_tk_string_t cmd_hello_tutu_hello =
+       UCG_CMD_TK_STRING(struct cmd_hello_tutu_result, hello, "hello");
+
+static ucg_cmd_tk_string_t cmd_hello_tutu_name1 =
+       UCG_CMD_TK_STRING(struct cmd_hello_tutu_result, name1, "tutu");
+
+static ucg_cmd_tk_string_t cmd_hello_tutu_name2 =
+       UCG_CMD_TK_STRING(struct cmd_hello_tutu_result, name2, NULL);
+
+static ucg_cmd_inst_t cmd_hello_tutu = {
+       .f = cmd_hello_tutu_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "Say hello to tutu and someone else",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_hello_tutu_hello,
+               (void *)&cmd_hello_tutu_name1,
+               (void *)&cmd_hello_tutu_name2,
+               NULL,
+       },
+};
+
+/**********************************************************/
+
+struct cmd_hello_toto_result {
+       ucg_cmd_fixed_string_t hello;
+       ucg_cmd_fixed_string_t name;
+       uint16_t count;
+};
+
+static void cmd_hello_toto_parsed(void *parsed_result,
+       struct ucg_cmd *cl, void *data)
+{
+       uint16_t i;
+       struct cmd_hello_toto_result *res = parsed_result;
+
+       (void)data;
+       for (i = 0; i < res->count; i++)
+               ucg_cmd_printf(cl, "hello %s\n", res->name);
+}
+
+static ucg_cmd_tk_string_t cmd_hello_toto_hello =
+       UCG_CMD_TK_STRING(struct cmd_hello_toto_result, hello, "hello");
+
+static ucg_cmd_tk_string_t cmd_hello_toto_name =
+       UCG_CMD_TK_STRING(struct cmd_hello_toto_result, name, "toto#titi");
+
+static ucg_cmd_tk_num_t cmd_hello_toto_count =
+       UCG_CMD_TK_NUM(struct cmd_hello_toto_result, count, UINT16);
+
+static ucg_cmd_inst_t cmd_hello_toto = {
+       .f = cmd_hello_toto_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "Say hello to toto or titi several times",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_hello_toto_hello,
+               (void *)&cmd_hello_toto_name,
+               (void *)&cmd_hello_toto_count,
+               NULL,
+       },
+};
+
+/****** CONTEXT (list of instruction) */
+
+ucg_cmd_ctx_t main_ctx = {
+       .name = "main",
+       .insts = {
+               &cmd_hello,
+               &cmd_hello_tutu,
+               &cmd_hello_toto,
+               NULL,
+       },
+};
diff --git a/examples/test-cmd/commands.h b/examples/test-cmd/commands.h
new file mode 100644 (file)
index 0000000..f80c5f6
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef COMMANDS_H_
+#define COMMANDS_H_
+
+#include <ucg_cmd_parse.h>
+
+extern ucg_cmd_ctx_t main_ctx;
+
+#endif /* COMMANDS_H_ */
diff --git a/examples/test-cmd/main.c b/examples/test-cmd/main.c
new file mode 100644 (file)
index 0000000..a7feaf6
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <ucg_irq.h>
+#include <ucg_delay.h>
+
+#include <ucg_cmd.h>
+#include <ucg_cmd_termios.h>
+
+#include "commands.h"
+
+#include "uart.h"
+
+#if defined(__ARM_EABI__)
+#include <stm32f4xx.h>
+
+static void led_on(void)
+{
+       GPIOD->ODR |= (1 << 13);
+}
+
+static void led_off(void)
+{
+       GPIOD->ODR &= (~(1 << 13));
+}
+
+static void target_init(void)
+{
+       /* enable the clock to GPIOD, and stall instruction pipeline as per
+        * errata 2.1.13 "Delay after an RCC peripheral clock enabling" */
+       RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
+       __asm("dsb");
+
+       /* set pin 13 to be general purpose output */
+       GPIOD->MODER = (1 << 26);
+}
+#elif defined(__AVR__)
+#include <avr/io.h>
+
+static void led_on(void)
+{
+       PORTB |= (1 << 5);
+}
+
+static void led_off(void)
+{
+       PORTB &= (~(1 << 5));
+}
+
+static void target_init(void)
+{
+       DDRB = (1 << 5);
+}
+#else
+static void led_on(void)
+{
+       printf("led on\n");
+}
+
+static void led_off(void)
+{
+       printf("led off\n");
+}
+
+static void target_init(void)
+{
+}
+#endif
+
+int main(void)
+{
+       unsigned i;
+       struct ucg_cmd cl;
+       const char *prompt = "\033[32mtest> \033[0m";
+       unsigned cl_flags = 0;
+
+       target_init();
+
+       /* toggle the pin */
+       for (i = 0; i < 3; i++) {
+               led_on();
+               ucg_delay_ms(500);
+               led_off();
+               ucg_delay_ms(500);
+       }
+
+       uart_init();
+       ucg_irq_unlock();
+       printf("hello\n");
+       ucg_delay_ms(500);
+
+#if defined( __AVR__) || defined(__ARM_EABI__)
+       cl_flags = UCG_CMD_F_IGNORE_EOF;
+#endif
+       ucg_cmd_init(&cl, &main_ctx, prompt, stdin, stdout);
+       if (ucg_cmd_termios_raw(&cl) < 0) {
+               printf("cannot set termios in raw mode\n");
+               return 1;
+       }
+
+       ucg_cmd_interact(&cl, cl_flags);
+       ucg_cmd_termios_restore(&cl);
+
+       return 0;
+}
diff --git a/examples/test-cmd/stm32_flash.ld b/examples/test-cmd/stm32_flash.ld
new file mode 100755 (executable)
index 0000000..350c05b
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+*****************************************************************************
+**
+**  File        : stm32_flash.ld
+**
+**  Abstract    : Linker script for STM32F407VG Device with
+**                1024KByte FLASH, 192KByte RAM
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used.
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Environment : Atollic TrueSTUDIO(R)
+**
+**  Distribution: The file is distributed \93as is,\94 without any warranty
+**                of any kind.
+**
+**  (c)Copyright Atollic AB.
+**  You may use this file as-is or modify it according to the needs of your
+**  project. Distribution of this file (unmodified or modified) is not
+**  permitted. Atollic AB permit registered Atollic TrueSTUDIO(R) users the
+**  rights to distribute the assembled, compiled & linked contents of this
+**  file as part of an application binary file, provided that it is built
+**  using the Atollic TrueSTUDIO(R) toolchain.
+**
+*****************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Highest address of the user mode stack */
+_estack = 0x20020000;    /* end of 128K RAM on AHB bus*/
+
+/* Generate a link error if heap and stack don't fit into RAM */
+_Min_Heap_Size = 0;      /* required amount of heap  */
+_Min_Stack_Size = 0x400; /* required amount of stack */
+
+/* Specify the memory areas */
+MEMORY
+{
+  FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 1024K
+  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 192K
+  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
+}
+
+/* Define output sections */
+SECTIONS
+{
+  /* The startup code goes first into FLASH */
+  .isr_vector :
+  {
+    . = ALIGN(4);
+    KEEP(*(.isr_vector)) /* Startup code */
+    . = ALIGN(4);
+  } >FLASH
+
+  /* The program code and other data goes into FLASH */
+  .text :
+  {
+    . = ALIGN(4);
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+       *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    . = ALIGN(4);
+    _etext = .;        /* define a global symbols at end of code */
+    _exit = .;
+  } >FLASH
+
+
+   .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
+    .ARM : {
+    __exidx_start = .;
+      *(.ARM.exidx*)
+      __exidx_end = .;
+    } >FLASH
+
+  .preinit_array     :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  } >FLASH
+  .init_array :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } >FLASH
+  .fini_array :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(.fini_array*))
+    KEEP (*(SORT(.fini_array.*)))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } >FLASH
+
+  /* used by the startup to initialize data */
+  _sidata = .;
+
+  /* Initialized data sections goes into RAM, load LMA copy after code */
+  .data : AT ( _sidata )
+  {
+    . = ALIGN(4);
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+
+    . = ALIGN(4);
+    _edata = .;        /* define a global symbol at data end */
+  } >RAM
+
+  /* Uninitialized data section */
+  . = ALIGN(4);
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss secion */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    . = ALIGN(4);
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough RAM left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(4);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    PROVIDE ( __end__ = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(4);
+  } >RAM
+
+  /* MEMORY_bank1 section, code must be located here explicitly            */
+  /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */
+  .memory_b1_text :
+  {
+    *(.mb1text)        /* .mb1text sections (code) */
+    *(.mb1text*)       /* .mb1text* sections (code)  */
+    *(.mb1rodata)      /* read-only data (constants) */
+    *(.mb1rodata*)
+  } >MEMORY_B1
+
+  /* Remove information from the standard libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/examples/test-cmd/stm32f4xx_conf.h b/examples/test-cmd/stm32f4xx_conf.h
new file mode 100644 (file)
index 0000000..74447a8
--- /dev/null
@@ -0,0 +1,94 @@
+/**
+  ******************************************************************************
+  * @file    stm32f4xx_conf.h  
+  * @author  MCD Application Team
+  * @version V1.0.0
+  * @date    19-September-2011
+  * @brief   Library configuration file.
+  ******************************************************************************
+  * @attention
+  *
+  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
+  ******************************************************************************
+  */ 
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32F4xx_CONF_H
+#define __STM32F4xx_CONF_H
+
+#if defined  (HSE_VALUE)
+/* Redefine the HSE value; it's equal to 8 MHz on the STM32F4-DISCOVERY Kit */
+ #undef HSE_VALUE
+ #define HSE_VALUE    ((uint32_t)8000000) 
+#endif /* HSE_VALUE */
+
+/* Includes ------------------------------------------------------------------*/
+/* Uncomment the line below to enable peripheral header file inclusion */
+#include "stm32f4xx_adc.h"
+#include "stm32f4xx_can.h"
+#include "stm32f4xx_crc.h"
+#include "stm32f4xx_cryp.h"
+#include "stm32f4xx_dac.h"
+#include "stm32f4xx_dbgmcu.h"
+#include "stm32f4xx_dcmi.h"
+#include "stm32f4xx_dma.h"
+#include "stm32f4xx_exti.h"
+#include "stm32f4xx_flash.h"
+#include "stm32f4xx_fsmc.h"
+#include "stm32f4xx_hash.h"
+#include "stm32f4xx_gpio.h"
+#include "stm32f4xx_i2c.h"
+#include "stm32f4xx_iwdg.h"
+#include "stm32f4xx_pwr.h"
+#include "stm32f4xx_rcc.h"
+#include "stm32f4xx_rng.h"
+#include "stm32f4xx_rtc.h"
+#include "stm32f4xx_sdio.h"
+#include "stm32f4xx_spi.h"
+#include "stm32f4xx_syscfg.h"
+#include "stm32f4xx_tim.h"
+#include "stm32f4xx_usart.h"
+#include "stm32f4xx_wwdg.h"
+#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/* If an external clock source is used, then the value of the following define 
+   should be set to the value of the external clock source, else, if no external 
+   clock is used, keep this define commented */
+/*#define I2S_EXTERNAL_CLOCK_VAL   12288000 */ /* Value of the external clock in Hz */
+
+
+/* Uncomment the line below to expanse the "assert_param" macro in the 
+   Standard Peripheral Library drivers code */
+/* #define USE_FULL_ASSERT    1 */
+
+/* Exported macro ------------------------------------------------------------*/
+#ifdef  USE_FULL_ASSERT
+
+/**
+  * @brief  The assert_param macro is used for function's parameters check.
+  * @param  expr: If expr is false, it calls assert_failed function
+  *   which reports the name of the source file and the source
+  *   line number of the call that failed. 
+  *   If expr is true, it returns no value.
+  * @retval None
+  */
+  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
+/* Exported functions ------------------------------------------------------- */
+  void assert_failed(uint8_t* file, uint32_t line);
+#else
+  #define assert_param(expr) ((void)0)
+#endif /* USE_FULL_ASSERT */
+
+#endif /* __STM32F4xx_CONF_H */
+
+/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
diff --git a/examples/test-cmd/system_stm32f4xx.c b/examples/test-cmd/system_stm32f4xx.c
new file mode 100644 (file)
index 0000000..98e8e04
--- /dev/null
@@ -0,0 +1,553 @@
+/**
+  ******************************************************************************
+  * @file    system_stm32f4xx.c
+  * @author  MCD Application Team
+  * @version V1.0.0
+  * @date    30-September-2011
+  * @brief   CMSIS Cortex-M4 Device Peripheral Access Layer System Source File.
+  *          This file contains the system clock configuration for STM32F4xx devices,
+  *          and is generated by the clock configuration tool
+  *          stm32f4xx_Clock_Configuration_V1.0.0.xls
+  *             
+  * 1.  This file provides two functions and one global variable to be called from 
+  *     user application:
+  *      - SystemInit(): Setups the system clock (System clock source, PLL Multiplier
+  *                      and Divider factors, AHB/APBx prescalers and Flash settings),
+  *                      depending on the configuration made in the clock xls tool. 
+  *                      This function is called at startup just after reset and 
+  *                      before branch to main program. This call is made inside
+  *                      the "startup_stm32f4xx.s" file.
+  *
+  *      - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
+  *                                  by the user application to setup the SysTick 
+  *                                  timer or configure other parameters.
+  *                                     
+  *      - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
+  *                                 be called whenever the core clock is changed
+  *                                 during program execution.
+  *
+  * 2. After each device reset the HSI (16 MHz) is used as system clock source.
+  *    Then SystemInit() function is called, in "startup_stm32f4xx.s" file, to
+  *    configure the system clock before to branch to main program.
+  *
+  * 3. If the system clock source selected by user fails to startup, the SystemInit()
+  *    function will do nothing and HSI still used as system clock source. User can 
+  *    add some code to deal with this issue inside the SetSysClock() function.
+  *
+  * 4. The default value of HSE crystal is set to 25MHz, refer to "HSE_VALUE" define
+  *    in "stm32f4xx.h" file. When HSE is used as system clock source, directly or
+  *    through PLL, and you are using different crystal you have to adapt the HSE
+  *    value to your own configuration.
+  *
+  * 5. This file configures the system clock as follows:
+  *=============================================================================
+  *=============================================================================
+  *        Supported STM32F4xx device revision    | Rev A
+  *-----------------------------------------------------------------------------
+  *        System Clock source                    | PLL (HSE)
+  *-----------------------------------------------------------------------------
+  *        SYSCLK(Hz)                             | 168000000
+  *-----------------------------------------------------------------------------
+  *        HCLK(Hz)                               | 168000000
+  *-----------------------------------------------------------------------------
+  *        AHB Prescaler                          | 1
+  *-----------------------------------------------------------------------------
+  *        APB1 Prescaler                         | 4
+  *-----------------------------------------------------------------------------
+  *        APB2 Prescaler                         | 2
+  *-----------------------------------------------------------------------------
+  *        HSE Frequency(Hz)                      | 25000000
+  *-----------------------------------------------------------------------------
+  *        PLL_M                                  | 25
+  *-----------------------------------------------------------------------------
+  *        PLL_N                                  | 336
+  *-----------------------------------------------------------------------------
+  *        PLL_P                                  | 2
+  *-----------------------------------------------------------------------------
+  *        PLL_Q                                  | 7
+  *-----------------------------------------------------------------------------
+  *        PLLI2S_N                               | NA
+  *-----------------------------------------------------------------------------
+  *        PLLI2S_R                               | NA
+  *-----------------------------------------------------------------------------
+  *        I2S input clock                        | NA
+  *-----------------------------------------------------------------------------
+  *        VDD(V)                                 | 3.3
+  *-----------------------------------------------------------------------------
+  *        Main regulator output voltage          | Scale1 mode
+  *-----------------------------------------------------------------------------
+  *        Flash Latency(WS)                      | 5
+  *-----------------------------------------------------------------------------
+  *        Prefetch Buffer                        | OFF
+  *-----------------------------------------------------------------------------
+  *        Instruction cache                      | ON
+  *-----------------------------------------------------------------------------
+  *        Data cache                             | ON
+  *-----------------------------------------------------------------------------
+  *        Require 48MHz for USB OTG FS,          | Enabled
+  *        SDIO and RNG clock                     |
+  *-----------------------------------------------------------------------------
+  *=============================================================================
+  ****************************************************************************** 
+  * @attention
+  *
+  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
+  ******************************************************************************
+  */
+
+/** @addtogroup CMSIS
+  * @{
+  */
+
+/** @addtogroup stm32f4xx_system
+  * @{
+  */  
+  
+/** @addtogroup STM32F4xx_System_Private_Includes
+  * @{
+  */
+
+#include "stm32f4xx.h"
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_TypesDefinitions
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Defines
+  * @{
+  */
+
+/************************* Miscellaneous Configuration ************************/
+/*!< Uncomment the following line if you need to use external SRAM mounted
+     on STM324xG_EVAL board as data memory  */
+/* #define DATA_IN_ExtSRAM */
+
+/*!< Uncomment the following line if you need to relocate your vector Table in
+     Internal SRAM. */
+/* #define VECT_TAB_SRAM */
+#define VECT_TAB_OFFSET  0x00 /*!< Vector Table base offset field. 
+                                   This value must be a multiple of 0x200. */
+/******************************************************************************/
+
+/************************* PLL Parameters *************************************/
+/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
+#define PLL_M      25
+#define PLL_N      336
+
+/* SYSCLK = PLL_VCO / PLL_P */
+#define PLL_P      2
+
+/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */
+#define PLL_Q      7
+
+/******************************************************************************/
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Macros
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Variables
+  * @{
+  */
+
+  uint32_t SystemCoreClock = 168000000;
+
+  __I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes
+  * @{
+  */
+
+static void SetSysClock(void);
+#ifdef DATA_IN_ExtSRAM
+  static void SystemInit_ExtMemCtl(void); 
+#endif /* DATA_IN_ExtSRAM */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Functions
+  * @{
+  */
+
+/**
+  * @brief  Setup the microcontroller system
+  *         Initialize the Embedded Flash Interface, the PLL and update the 
+  *         SystemFrequency variable.
+  * @param  None
+  * @retval None
+  */
+void SystemInit(void)
+{
+  /* FPU settings ------------------------------------------------------------*/
+  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
+    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
+  #endif
+
+  /* Reset the RCC clock configuration to the default reset state ------------*/
+  /* Set HSION bit */
+  RCC->CR |= (uint32_t)0x00000001;
+
+  /* Reset CFGR register */
+  RCC->CFGR = 0x00000000;
+
+  /* Reset HSEON, CSSON and PLLON bits */
+  RCC->CR &= (uint32_t)0xFEF6FFFF;
+
+  /* Reset PLLCFGR register */
+  RCC->PLLCFGR = 0x24003010;
+
+  /* Reset HSEBYP bit */
+  RCC->CR &= (uint32_t)0xFFFBFFFF;
+
+  /* Disable all interrupts */
+  RCC->CIR = 0x00000000;
+
+#ifdef DATA_IN_ExtSRAM
+  SystemInit_ExtMemCtl(); 
+#endif /* DATA_IN_ExtSRAM */
+         
+  /* Configure the System clock source, PLL Multiplier and Divider factors, 
+     AHB/APBx prescalers and Flash settings ----------------------------------*/
+  SetSysClock();
+
+  /* Configure the Vector Table location add offset address ------------------*/
+#ifdef VECT_TAB_SRAM
+  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
+#else
+  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
+#endif
+}
+
+/**
+   * @brief  Update SystemCoreClock variable according to Clock Register Values.
+  *         The SystemCoreClock variable contains the core clock (HCLK), it can
+  *         be used by the user application to setup the SysTick timer or configure
+  *         other parameters.
+  *           
+  * @note   Each time the core clock (HCLK) changes, this function must be called
+  *         to update SystemCoreClock variable value. Otherwise, any configuration
+  *         based on this variable will be incorrect.         
+  *     
+  * @note   - The system frequency computed by this function is not the real 
+  *           frequency in the chip. It is calculated based on the predefined 
+  *           constant and the selected clock source:
+  *             
+  *           - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
+  *                                              
+  *           - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
+  *                          
+  *           - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) 
+  *             or HSI_VALUE(*) multiplied/divided by the PLL factors.
+  *         
+  *         (*) HSI_VALUE is a constant defined in stm32f4xx.h file (default value
+  *             16 MHz) but the real value may vary depending on the variations
+  *             in voltage and temperature.   
+  *    
+  *         (**) HSE_VALUE is a constant defined in stm32f4xx.h file (default value
+  *              25 MHz), user has to ensure that HSE_VALUE is same as the real
+  *              frequency of the crystal used. Otherwise, this function may
+  *              have wrong result.
+  *                
+  *         - The result of this function could be not correct when using fractional
+  *           value for HSE crystal.
+  *     
+  * @param  None
+  * @retval None
+  */
+void SystemCoreClockUpdate(void)
+{
+  uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;
+  
+  /* Get SYSCLK source -------------------------------------------------------*/
+  tmp = RCC->CFGR & RCC_CFGR_SWS;
+
+  switch (tmp)
+  {
+    case 0x00:  /* HSI used as system clock source */
+      SystemCoreClock = HSI_VALUE;
+      break;
+    case 0x04:  /* HSE used as system clock source */
+      SystemCoreClock = HSE_VALUE;
+      break;
+    case 0x08:  /* PLL used as system clock source */
+
+      /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
+         SYSCLK = PLL_VCO / PLL_P
+         */    
+      pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
+      pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
+      
+      if (pllsource != 0)
+      {
+        /* HSE used as PLL clock source */
+        pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
+      }
+      else
+      {
+        /* HSI used as PLL clock source */
+        pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);      
+      }
+
+      pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;
+      SystemCoreClock = pllvco/pllp;
+      break;
+    default:
+      SystemCoreClock = HSI_VALUE;
+      break;
+  }
+  /* Compute HCLK frequency --------------------------------------------------*/
+  /* Get HCLK prescaler */
+  tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
+  /* HCLK frequency */
+  SystemCoreClock >>= tmp;
+}
+
+/**
+  * @brief  Configures the System clock source, PLL Multiplier and Divider factors, 
+  *         AHB/APBx prescalers and Flash settings
+  * @Note   This function should be called only once the RCC clock configuration  
+  *         is reset to the default reset state (done in SystemInit() function).   
+  * @param  None
+  * @retval None
+  */
+static void SetSysClock(void)
+{
+/******************************************************************************/
+/*            PLL (clocked by HSE) used as System clock source                */
+/******************************************************************************/
+  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+  
+  /* Enable HSE */
+  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
+  /* Wait till HSE is ready and if Time out is reached exit */
+  do
+  {
+    HSEStatus = RCC->CR & RCC_CR_HSERDY;
+    StartUpCounter++;
+  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
+  {
+    HSEStatus = (uint32_t)0x01;
+  }
+  else
+  {
+    HSEStatus = (uint32_t)0x00;
+  }
+
+  if (HSEStatus == (uint32_t)0x01)
+  {
+    /* Select regulator voltage output Scale 1 mode, System frequency up to 168 MHz */
+    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
+    PWR->CR |= PWR_CR_VOS;
+
+    /* HCLK = SYSCLK / 1*/
+    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
+      
+    /* PCLK2 = HCLK / 2*/
+    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
+    
+    /* PCLK1 = HCLK / 4*/
+    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
+
+    /* Configure the main PLL */
+    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
+                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
+
+    /* Enable the main PLL */
+    RCC->CR |= RCC_CR_PLLON;
+
+    /* Wait till the main PLL is ready */
+    while((RCC->CR & RCC_CR_PLLRDY) == 0)
+    {
+    }
+   
+    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
+    FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
+
+    /* Select the main PLL as system clock source */
+    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
+    RCC->CFGR |= RCC_CFGR_SW_PLL;
+
+    /* Wait till the main PLL is used as system clock source */
+    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
+    {
+    }
+  }
+  else
+  { /* If HSE fails to start-up, the application will have wrong clock
+         configuration. User can add here some code to deal with this error */
+  }
+
+}
+
+/**
+  * @brief  Setup the external memory controller. Called in startup_stm32f4xx.s 
+  *          before jump to __main
+  * @param  None
+  * @retval None
+  */ 
+#ifdef DATA_IN_ExtSRAM
+/**
+  * @brief  Setup the external memory controller.
+  *         Called in startup_stm32f4xx.s before jump to main.
+  *         This function configures the external SRAM mounted on STM324xG_EVAL board
+  *         This SRAM will be used as program data memory (including heap and stack).
+  * @param  None
+  * @retval None
+  */
+void SystemInit_ExtMemCtl(void)
+{
+/*-- GPIOs Configuration -----------------------------------------------------*/
+/*
+ +-------------------+--------------------+------------------+------------------+
+ +                       SRAM pins assignment                                   +
+ +-------------------+--------------------+------------------+------------------+
+ | PD0  <-> FSMC_D2  | PE0  <-> FSMC_NBL0 | PF0  <-> FSMC_A0 | PG0 <-> FSMC_A10 | 
+ | PD1  <-> FSMC_D3  | PE1  <-> FSMC_NBL1 | PF1  <-> FSMC_A1 | PG1 <-> FSMC_A11 | 
+ | PD4  <-> FSMC_NOE | PE3  <-> FSMC_A19  | PF2  <-> FSMC_A2 | PG2 <-> FSMC_A12 | 
+ | PD5  <-> FSMC_NWE | PE4  <-> FSMC_A20  | PF3  <-> FSMC_A3 | PG3 <-> FSMC_A13 | 
+ | PD8  <-> FSMC_D13 | PE7  <-> FSMC_D4   | PF4  <-> FSMC_A4 | PG4 <-> FSMC_A14 | 
+ | PD9  <-> FSMC_D14 | PE8  <-> FSMC_D5   | PF5  <-> FSMC_A5 | PG5 <-> FSMC_A15 | 
+ | PD10 <-> FSMC_D15 | PE9  <-> FSMC_D6   | PF12 <-> FSMC_A6 | PG9 <-> FSMC_NE2 | 
+ | PD11 <-> FSMC_A16 | PE10 <-> FSMC_D7   | PF13 <-> FSMC_A7 |------------------+
+ | PD12 <-> FSMC_A17 | PE11 <-> FSMC_D8   | PF14 <-> FSMC_A8 | 
+ | PD13 <-> FSMC_A18 | PE12 <-> FSMC_D9   | PF15 <-> FSMC_A9 | 
+ | PD14 <-> FSMC_D0  | PE13 <-> FSMC_D10  |------------------+
+ | PD15 <-> FSMC_D1  | PE14 <-> FSMC_D11  |
+ |                   | PE15 <-> FSMC_D12  |
+ +-------------------+--------------------+
+*/
+   /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */
+  RCC->AHB1ENR   = 0x00000078;
+  
+  /* Connect PDx pins to FSMC Alternate function */
+  GPIOD->AFR[0]  = 0x00cc00cc;
+  GPIOD->AFR[1]  = 0xcc0ccccc;
+  /* Configure PDx pins in Alternate function mode */  
+  GPIOD->MODER   = 0xaaaa0a0a;
+  /* Configure PDx pins speed to 100 MHz */  
+  GPIOD->OSPEEDR = 0xffff0f0f;
+  /* Configure PDx pins Output type to push-pull */  
+  GPIOD->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PDx pins */ 
+  GPIOD->PUPDR   = 0x00000000;
+
+  /* Connect PEx pins to FSMC Alternate function */
+  GPIOE->AFR[0]  = 0xc00cc0cc;
+  GPIOE->AFR[1]  = 0xcccccccc;
+  /* Configure PEx pins in Alternate function mode */ 
+  GPIOE->MODER   = 0xaaaa828a;
+  /* Configure PEx pins speed to 100 MHz */ 
+  GPIOE->OSPEEDR = 0xffffc3cf;
+  /* Configure PEx pins Output type to push-pull */  
+  GPIOE->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PEx pins */ 
+  GPIOE->PUPDR   = 0x00000000;
+
+  /* Connect PFx pins to FSMC Alternate function */
+  GPIOF->AFR[0]  = 0x00cccccc;
+  GPIOF->AFR[1]  = 0xcccc0000;
+  /* Configure PFx pins in Alternate function mode */   
+  GPIOF->MODER   = 0xaa000aaa;
+  /* Configure PFx pins speed to 100 MHz */ 
+  GPIOF->OSPEEDR = 0xff000fff;
+  /* Configure PFx pins Output type to push-pull */  
+  GPIOF->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PFx pins */ 
+  GPIOF->PUPDR   = 0x00000000;
+
+  /* Connect PGx pins to FSMC Alternate function */
+  GPIOG->AFR[0]  = 0x00cccccc;
+  GPIOG->AFR[1]  = 0x000000c0;
+  /* Configure PGx pins in Alternate function mode */ 
+  GPIOG->MODER   = 0x00080aaa;
+  /* Configure PGx pins speed to 100 MHz */ 
+  GPIOG->OSPEEDR = 0x000c0fff;
+  /* Configure PGx pins Output type to push-pull */  
+  GPIOG->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PGx pins */ 
+  GPIOG->PUPDR   = 0x00000000;
+  
+/*-- FSMC Configuration ------------------------------------------------------*/
+  /* Enable the FSMC interface clock */
+  RCC->AHB3ENR         = 0x00000001;
+
+  /* Configure and enable Bank1_SRAM2 */
+  FSMC_Bank1->BTCR[2]  = 0x00001015;
+  FSMC_Bank1->BTCR[3]  = 0x00010603;
+  FSMC_Bank1E->BWTR[2] = 0x0fffffff;
+/*
+  Bank1_SRAM2 is configured as follow:
+
+  p.FSMC_AddressSetupTime = 3;
+  p.FSMC_AddressHoldTime = 0;
+  p.FSMC_DataSetupTime = 6;
+  p.FSMC_BusTurnAroundDuration = 1;
+  p.FSMC_CLKDivision = 0;
+  p.FSMC_DataLatency = 0;
+  p.FSMC_AccessMode = FSMC_AccessMode_A;
+
+  FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2;
+  FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_PSRAM;
+  FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
+  FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;  
+  FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
+  FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
+  FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
+  FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;
+  FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;
+*/
+  
+}
+#endif /* DATA_IN_ExtSRAM */
+
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+  
+/**
+  * @}
+  */    
+/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
diff --git a/examples/test-cmd/uart.c b/examples/test-cmd/uart.c
new file mode 100644 (file)
index 0000000..5500782
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(__ARM_EABI__)
+#include "stm32f4xx.h"
+#elif defined(__AVR__)
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <ucg_irq.h>
+#include <ucg_cirbuf.h>
+#include <ucg_uart.h>
+
+#include "uart.h"
+
+#if defined(__ARM_EABI__) || defined(__AVR__)
+/* rx & tx buffers */
+static char rx_buf[64];
+static struct ucg_cirbuf rx_cirbuf;
+static char tx_buf[64];
+static struct ucg_cirbuf tx_cirbuf;
+/* generic uart struct */
+struct ucg_uart main_uart;
+#endif
+
+#if defined(__ARM_EABI__)
+
+#include <ucg_gloss_chardev.h>
+#include <ucg_stm32_uart.h>
+
+static struct ucg_stm32_uart stm32_uart_data = {
+       .uart = USART2,
+       .rcc_uart = RCC_APB1Periph_USART2,
+       .rcc_gpio = RCC_AHB1Periph_GPIOA,
+       .gpio = GPIOA,
+       .gpio_af = GPIO_AF_USART2,
+       .gpio_pins = GPIO_Pin_2 | GPIO_Pin_3,
+       .gpio_speed = GPIO_Speed_25MHz,
+       .irq = USART2_IRQn,
+       .irq_preempt_prio = 2,
+       .irq_sub_prio = 0,
+};
+
+static _ssize_t
+uart_write_r(__attribute__((unused)) struct _reent *r,
+       __attribute__((unused)) int fd,
+       const void *ptr, size_t len)
+{
+       size_t i;
+       const char *buf = ptr;
+
+       for (i = 0; i < len; i++)
+               ucg_uart_send(&main_uart, buf[i], WAIT);
+
+       return len;
+}
+
+static _ssize_t
+uart_read_r(__attribute__((unused)) struct _reent *r,
+       __attribute__((unused)) int fd,
+       void *ptr, size_t len)
+{
+       int c;
+       char *buf = ptr;
+
+       (void)len;
+       c = ucg_uart_recv(&main_uart, NOWAIT);
+       if (c < 0) {
+               r->_errno = EAGAIN;
+               return 0;
+       }
+       buf[0] = c;
+       return 1;
+}
+
+void USART2_IRQHandler(void)
+{
+       if ((USART2->SR & USART_FLAG_TXE))
+               ucg_uart_tx_intr(&main_uart);
+       if ((USART2->SR & USART_FLAG_RXNE))
+               ucg_uart_rx_intr(&main_uart);
+}
+
+struct ucg_chardev uart_stdin_dev = {
+       .name = "stdin",
+       .open_r = NULL,
+       .close_r = NULL,
+       .read_r = uart_read_r,
+       .write_r = NULL,
+};
+
+struct ucg_chardev uart_stdout_dev = {
+       .name = "stdout",
+       .open_r = NULL,
+       .close_r = NULL,
+       .read_r = NULL,
+       .write_r = uart_write_r,
+};
+
+struct ucg_chardev uart_stderr_dev = {
+       .name = "stderr",
+       .open_r = NULL,
+       .close_r = NULL,
+       .read_r = NULL,
+       .write_r = uart_write_r,
+};
+
+static void uart_register_stdio(void)
+{
+       ucg_chardev_register(&uart_stdin_dev);
+       ucg_chardev_register(&uart_stdout_dev);
+       ucg_chardev_register(&uart_stderr_dev);
+
+       /* Disable buffering */
+       setbuf(stdin, NULL);
+       setbuf(stdout, NULL);
+       setbuf(stderr, NULL);
+}
+
+#elif defined(__AVR__)
+
+#include <ucg_avr_uart.h>
+/* avr-specific uart struct */
+static struct ucg_avr_uart avr_uart_data = {
+       .reg_udr = &UDR0,
+       .reg_ucsra = &UCSR0A,
+       .reg_ucsrb = &UCSR0B,
+       .reg_ucsrc = &UCSR0C,
+       .reg_ubrrl = &UBRR0L,
+       .reg_ubrrh = &UBRR0H,
+       .bit_udre = UDRE0,
+       .bit_rxc = RXC0,
+       .bit_udrie = UDRIE0,
+       .bit_rxen = RXEN0,
+       .bit_txen = TXEN0,
+       .bit_rxcie = RXCIE0,
+       .bit_u2x = U2X0,
+};
+
+/* send on stdout */
+static int std_send(char c, FILE *f)
+{
+       (void)f;
+       ucg_uart_send(&main_uart, c, WAIT);
+       return 0;
+}
+
+/* recv on stdin */
+static int std_recv(FILE *f)
+{
+       int16_t c;
+
+       (void)f;
+       c = ucg_uart_recv(&main_uart, NOWAIT);
+       if (c < 0)
+               return _FDEV_EOF;
+
+       return c;
+}
+
+SIGNAL(USART_RX_vect)
+{
+       ucg_uart_rx_intr(&main_uart);
+}
+
+SIGNAL(USART_UDRE_vect)
+{
+       ucg_uart_tx_intr(&main_uart);
+}
+#endif
+
+int uart_init(void)
+{
+       int ret = 0;
+#if defined(__ARM_EABI__) || defined(__AVR__)
+       struct ucg_uart_config conf;
+       const void *uart_ops;
+       void *uart_data;
+
+#if defined(__ARM_EABI__)
+       uart_ops = &stm32_uart_ops;
+       uart_data = &stm32_uart_data;
+#elif defined(__AVR__)
+       uart_ops = &avr_uart_ops;
+       uart_data = &avr_uart_data;
+#endif
+
+       ret = ucg_uart_init(&main_uart, uart_ops, uart_data,
+               &rx_cirbuf, rx_buf, sizeof(rx_buf),
+               &tx_cirbuf, tx_buf, sizeof(tx_buf));
+       if (ret < 0)
+               return ret;
+
+       ucg_uart_getconf(&main_uart, &conf);
+       conf.baudrate = 57600;
+       ret = ucg_uart_setconf(&main_uart, &conf);
+       if (ret < 0)
+               return ret;
+
+#if defined(__ARM_EABI__)
+       uart_register_stdio();
+#elif defined(__AVR__)
+       fdevopen(std_send, std_recv);
+#endif
+#endif
+       return ret;
+}
diff --git a/examples/test-cmd/uart.h b/examples/test-cmd/uart.h
new file mode 100644 (file)
index 0000000..ca1a139
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UART_H_
+#define UART_H_
+
+#include <stdio.h>
+
+int uart_init(void);
+
+#endif
diff --git a/examples/test-mk/Makefile b/examples/test-mk/Makefile
new file mode 100644 (file)
index 0000000..f278945
--- /dev/null
@@ -0,0 +1,58 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+UCGINE ?= $(abspath ../..)
+
+# output path with trailing slash
+O ?= build/
+
+CFLAGS  = -g -O2 -Wall
+CFLAGS += -Idir
+
+cflags-$(O)titi.o := -DTEST
+
+obj-y-$(O)target.o := dir/titi.c dir/toto.c
+
+obj-y-$(O)obj.o := $(O)target.o
+
+ar-y-$(O)toto.a := $(O)obj.o
+exe-y-$(O)prog := main.c $(O)obj.o
+
+copy-y-$(O)install/ := $(O)toto.a $(O)prog
+copy-y-$(O)install/2/toto.a := $(O)toto.a
+slink-y-$(O)install/ := dir/toto.h
+
+subdir-y := test1 test2
+TOTO ?= 1
+mkflags-test1 := TOTO=$(TOTO)
+
+include $(UCGINE)/mk/ucgine.mk
+
+all: _ucgine_all
+
+clean: _ucgine_clean
+
+.PHONY: clean all
diff --git a/examples/test-mk/dir/titi.c b/examples/test-mk/dir/titi.c
new file mode 100644 (file)
index 0000000..05cc3a4
--- /dev/null
@@ -0,0 +1,4 @@
+int titi(void)
+{
+       return 0;
+}
diff --git a/examples/test-mk/dir/toto.c b/examples/test-mk/dir/toto.c
new file mode 100644 (file)
index 0000000..8b533ec
--- /dev/null
@@ -0,0 +1,4 @@
+int toto(void)
+{
+       return 0;
+}
diff --git a/examples/test-mk/dir/toto.h b/examples/test-mk/dir/toto.h
new file mode 100644 (file)
index 0000000..466fbd2
--- /dev/null
@@ -0,0 +1 @@
+int toto(void);
diff --git a/examples/test-mk/main.c b/examples/test-mk/main.c
new file mode 100644 (file)
index 0000000..31dbf45
--- /dev/null
@@ -0,0 +1,4 @@
+int main(void)
+{
+       return 0;
+}
diff --git a/examples/test-mk/test1/Makefile b/examples/test-mk/test1/Makefile
new file mode 100644 (file)
index 0000000..7386ae6
--- /dev/null
@@ -0,0 +1,43 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+UCGINE ?= $(abspath ../../..)
+
+# output path with trailing slash
+O = build/
+
+CFLAGS  = -g -O2 -Wall -DTOTO=$(TOTO)
+CFLAGS += -Idir
+
+exe-y-$(O)prog := main.c
+
+include $(UCGINE)/mk/ucgine.mk
+
+all: _ucgine_all
+
+clean: _ucgine_clean
+
+.PHONY: clean all
diff --git a/examples/test-mk/test1/main.c b/examples/test-mk/test1/main.c
new file mode 100644 (file)
index 0000000..31dbf45
--- /dev/null
@@ -0,0 +1,4 @@
+int main(void)
+{
+       return 0;
+}
diff --git a/examples/test-mk/test2/Makefile b/examples/test-mk/test2/Makefile
new file mode 100644 (file)
index 0000000..e874674
--- /dev/null
@@ -0,0 +1,43 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+UCGINE ?= $(abspath ../../..)
+
+# output path with trailing slash
+O = build/
+
+CFLAGS  = -g -O2 -Wall
+CFLAGS += -Idir
+
+exe-y-$(O)prog := main.c
+
+include $(UCGINE)/mk/ucgine.mk
+
+all: _ucgine_all
+
+clean: _ucgine_clean
+
+.PHONY: clean all
diff --git a/examples/test-mk/test2/main.c b/examples/test-mk/test2/main.c
new file mode 100644 (file)
index 0000000..31dbf45
--- /dev/null
@@ -0,0 +1,4 @@
+int main(void)
+{
+       return 0;
+}
diff --git a/examples/test-uart/Makefile b/examples/test-uart/Makefile
new file mode 100644 (file)
index 0000000..780187f
--- /dev/null
@@ -0,0 +1,114 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+UCGINE ?= $(abspath ../..)
+include $(UCGINE)/mk/ucgine-pre.mk
+
+O ?= $(CURDIR)/build
+PROG = $(O)/test-uart
+
+CROSS = arm-none-eabi-
+
+CFLAGS  = -g -O2 -Wall
+CFLAGS += -I.
+
+ifeq ($(CROSS),avr-)
+MCU = atmega328p
+F_CPU = 8000000UL
+AVRDUDE_PORT = /dev/ttyUSB0
+AVRDUDE_PROG = arduino
+AVRDUDE_BAUD = 57600
+CFLAGS += -I$(UCGINE)/arch/avr/include
+CFLAGS += -I$(UCGINE)/arch/avr/uart/include
+CFLAGS += -mmcu=$(MCU)
+CFLAGS += -DF_CPU=$(F_CPU)
+LDFLAGS += -mmcu=$(MCU)
+# uart
+exe-y-$(PROG) += $(UCGINE)/arch/avr/uart/ucg_avr_uart.c
+endif
+
+ifeq ($(CROSS),arm-none-eabi-)
+STLINK ?= /home/zer0/projects/stm32/stlink
+STM_COMMON ?= /home/zer0/projects/stm32/stm32_discovery_arm_gcc/STM32F4-Discovery_FW_V1.1.0
+CFLAGS += -DUSE_STDPERIPH_DRIVER -Tstm32_flash.ld
+CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
+CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
+CFLAGS += -I$(STM_COMMON)/Utilities/STM32F4-Discovery
+CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/Include
+CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Include
+CFLAGS += -I$(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/inc
+CFLAGS += -I$(UCGINE)/lib/gloss/include
+CFLAGS += -I$(UCGINE)/arch/stm32/include
+CFLAGS += -I$(UCGINE)/arch/stm32/uart/include
+LDFLAGS  = -Tstm32_flash.ld --specs=rdimon.specs -lc
+LDFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
+LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
+# stm32 standard periph lib
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_exti.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_gpio.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_rcc.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_usart.c
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/misc.c
+# startup file
+exe-y-$(PROG) += $(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Source/Templates/TrueSTUDIO/startup_stm32f4xx.s
+# local files
+exe-y-$(PROG) += system_stm32f4xx.c
+# gloss
+exe-y-$(PROG) += $(UCGINE)/lib/gloss/ucg_gloss_stubs.c
+exe-y-$(PROG) += $(UCGINE)/lib/gloss/ucg_gloss_chardev.c
+# uart
+exe-y-$(PROG) += $(UCGINE)/arch/stm32/uart/ucg_stm32_uart.c
+endif
+
+# cirbuf
+CFLAGS += -I$(UCGINE)/lib/cirbuf/include
+exe-y-$(PROG) += $(UCGINE)/lib/cirbuf/ucg_cirbuf.c
+# uart
+CFLAGS += -I$(UCGINE)/lib/uart/include
+exe-y-$(PROG) += $(UCGINE)/lib/uart/ucg_uart.c
+
+exe-y-$(PROG) += uart.c main.c
+
+objcopy-hex-y-$(PROG).hex := $(PROG)
+objcopy-bin-y-$(PROG).bin := $(PROG)
+
+include $(UCGINE)/mk/ucgine-post.mk
+
+.PHONY: all
+all: $(all-targets)
+
+.PHONY: clean
+clean: _ucgine_clean
+
+.PHONY: burn
+burn: all
+ifeq ($(CROSS),arm-none-eabi-)
+       $(STLINK)/st-flash write $(PROG).bin 0x8000000
+endif
+ifeq ($(CROSS),avr-)
+         avrdude -e -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROG) \
+               -b $(AVRDUDE_BAUD) -U flash:w:$(PROG):e
+endif
diff --git a/examples/test-uart/main.c b/examples/test-uart/main.c
new file mode 100644 (file)
index 0000000..ff82841
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ucg_delay.h>
+#include <ucg_irq.h>
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "uart.h"
+
+#if defined(__ARM_EABI__)
+#include <stm32f4xx.h>
+
+static void led_on(void)
+{
+       GPIOD->ODR |= (1 << 13);
+}
+
+static void led_off(void)
+{
+       GPIOD->ODR &= (~(1 << 13));
+}
+
+static void target_init(void)
+{
+       /* enable the clock to GPIOD, and stall instruction pipeline as per
+        * errata 2.1.13 "Delay after an RCC peripheral clock enabling" */
+       RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
+       __asm("dsb");
+
+       /* set pin 13 to be general purpose output */
+       GPIOD->MODER = (1 << 26);
+}
+#elif defined(__AVR__)
+#include <avr/io.h>
+
+static void led_on(void)
+{
+       PORTB |= (1 << 5);
+}
+
+static void led_off(void)
+{
+       PORTB &= (~(1 << 5));
+}
+
+static void target_init(void)
+{
+       DDRB = (1 << 5);
+}
+#endif
+
+static void big_print(void)
+{
+       printf( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n"
+               "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n"
+               "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n"
+               "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n");
+}
+
+int main(void)
+{
+       unsigned i;
+       char c;
+       int ret;
+
+       target_init();
+
+       /* toggle the pin */
+       for (i = 0; i < 3; i++) {
+               led_on();
+               ucg_delay_ms(500);
+               led_off();
+               ucg_delay_ms(500);
+       }
+
+       uart_init();
+       ucg_irq_unlock();
+
+       big_print();
+
+       while (1) {
+               ret = fread(&c, 1, 1, stdin);
+               if (ret == 1) {
+                       if (c == 'x')
+                               big_print();
+                       else
+                               fwrite(&c, 1, 1, stdout);
+               } else if (ret == 0)
+                       clearerr(stdin);
+       }
+
+       return 0;
+}
diff --git a/examples/test-uart/stm32_flash.ld b/examples/test-uart/stm32_flash.ld
new file mode 100755 (executable)
index 0000000..350c05b
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+*****************************************************************************
+**
+**  File        : stm32_flash.ld
+**
+**  Abstract    : Linker script for STM32F407VG Device with
+**                1024KByte FLASH, 192KByte RAM
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used.
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Environment : Atollic TrueSTUDIO(R)
+**
+**  Distribution: The file is distributed \93as is,\94 without any warranty
+**                of any kind.
+**
+**  (c)Copyright Atollic AB.
+**  You may use this file as-is or modify it according to the needs of your
+**  project. Distribution of this file (unmodified or modified) is not
+**  permitted. Atollic AB permit registered Atollic TrueSTUDIO(R) users the
+**  rights to distribute the assembled, compiled & linked contents of this
+**  file as part of an application binary file, provided that it is built
+**  using the Atollic TrueSTUDIO(R) toolchain.
+**
+*****************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Highest address of the user mode stack */
+_estack = 0x20020000;    /* end of 128K RAM on AHB bus*/
+
+/* Generate a link error if heap and stack don't fit into RAM */
+_Min_Heap_Size = 0;      /* required amount of heap  */
+_Min_Stack_Size = 0x400; /* required amount of stack */
+
+/* Specify the memory areas */
+MEMORY
+{
+  FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 1024K
+  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 192K
+  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
+}
+
+/* Define output sections */
+SECTIONS
+{
+  /* The startup code goes first into FLASH */
+  .isr_vector :
+  {
+    . = ALIGN(4);
+    KEEP(*(.isr_vector)) /* Startup code */
+    . = ALIGN(4);
+  } >FLASH
+
+  /* The program code and other data goes into FLASH */
+  .text :
+  {
+    . = ALIGN(4);
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+       *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    . = ALIGN(4);
+    _etext = .;        /* define a global symbols at end of code */
+    _exit = .;
+  } >FLASH
+
+
+   .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
+    .ARM : {
+    __exidx_start = .;
+      *(.ARM.exidx*)
+      __exidx_end = .;
+    } >FLASH
+
+  .preinit_array     :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  } >FLASH
+  .init_array :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } >FLASH
+  .fini_array :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(.fini_array*))
+    KEEP (*(SORT(.fini_array.*)))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } >FLASH
+
+  /* used by the startup to initialize data */
+  _sidata = .;
+
+  /* Initialized data sections goes into RAM, load LMA copy after code */
+  .data : AT ( _sidata )
+  {
+    . = ALIGN(4);
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+
+    . = ALIGN(4);
+    _edata = .;        /* define a global symbol at data end */
+  } >RAM
+
+  /* Uninitialized data section */
+  . = ALIGN(4);
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss secion */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    . = ALIGN(4);
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough RAM left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(4);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    PROVIDE ( __end__ = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(4);
+  } >RAM
+
+  /* MEMORY_bank1 section, code must be located here explicitly            */
+  /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */
+  .memory_b1_text :
+  {
+    *(.mb1text)        /* .mb1text sections (code) */
+    *(.mb1text*)       /* .mb1text* sections (code)  */
+    *(.mb1rodata)      /* read-only data (constants) */
+    *(.mb1rodata*)
+  } >MEMORY_B1
+
+  /* Remove information from the standard libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/examples/test-uart/stm32f4xx_conf.h b/examples/test-uart/stm32f4xx_conf.h
new file mode 100644 (file)
index 0000000..74447a8
--- /dev/null
@@ -0,0 +1,94 @@
+/**
+  ******************************************************************************
+  * @file    stm32f4xx_conf.h  
+  * @author  MCD Application Team
+  * @version V1.0.0
+  * @date    19-September-2011
+  * @brief   Library configuration file.
+  ******************************************************************************
+  * @attention
+  *
+  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
+  ******************************************************************************
+  */ 
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32F4xx_CONF_H
+#define __STM32F4xx_CONF_H
+
+#if defined  (HSE_VALUE)
+/* Redefine the HSE value; it's equal to 8 MHz on the STM32F4-DISCOVERY Kit */
+ #undef HSE_VALUE
+ #define HSE_VALUE    ((uint32_t)8000000) 
+#endif /* HSE_VALUE */
+
+/* Includes ------------------------------------------------------------------*/
+/* Uncomment the line below to enable peripheral header file inclusion */
+#include "stm32f4xx_adc.h"
+#include "stm32f4xx_can.h"
+#include "stm32f4xx_crc.h"
+#include "stm32f4xx_cryp.h"
+#include "stm32f4xx_dac.h"
+#include "stm32f4xx_dbgmcu.h"
+#include "stm32f4xx_dcmi.h"
+#include "stm32f4xx_dma.h"
+#include "stm32f4xx_exti.h"
+#include "stm32f4xx_flash.h"
+#include "stm32f4xx_fsmc.h"
+#include "stm32f4xx_hash.h"
+#include "stm32f4xx_gpio.h"
+#include "stm32f4xx_i2c.h"
+#include "stm32f4xx_iwdg.h"
+#include "stm32f4xx_pwr.h"
+#include "stm32f4xx_rcc.h"
+#include "stm32f4xx_rng.h"
+#include "stm32f4xx_rtc.h"
+#include "stm32f4xx_sdio.h"
+#include "stm32f4xx_spi.h"
+#include "stm32f4xx_syscfg.h"
+#include "stm32f4xx_tim.h"
+#include "stm32f4xx_usart.h"
+#include "stm32f4xx_wwdg.h"
+#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/* If an external clock source is used, then the value of the following define 
+   should be set to the value of the external clock source, else, if no external 
+   clock is used, keep this define commented */
+/*#define I2S_EXTERNAL_CLOCK_VAL   12288000 */ /* Value of the external clock in Hz */
+
+
+/* Uncomment the line below to expanse the "assert_param" macro in the 
+   Standard Peripheral Library drivers code */
+/* #define USE_FULL_ASSERT    1 */
+
+/* Exported macro ------------------------------------------------------------*/
+#ifdef  USE_FULL_ASSERT
+
+/**
+  * @brief  The assert_param macro is used for function's parameters check.
+  * @param  expr: If expr is false, it calls assert_failed function
+  *   which reports the name of the source file and the source
+  *   line number of the call that failed. 
+  *   If expr is true, it returns no value.
+  * @retval None
+  */
+  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
+/* Exported functions ------------------------------------------------------- */
+  void assert_failed(uint8_t* file, uint32_t line);
+#else
+  #define assert_param(expr) ((void)0)
+#endif /* USE_FULL_ASSERT */
+
+#endif /* __STM32F4xx_CONF_H */
+
+/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
diff --git a/examples/test-uart/system_stm32f4xx.c b/examples/test-uart/system_stm32f4xx.c
new file mode 100644 (file)
index 0000000..98e8e04
--- /dev/null
@@ -0,0 +1,553 @@
+/**
+  ******************************************************************************
+  * @file    system_stm32f4xx.c
+  * @author  MCD Application Team
+  * @version V1.0.0
+  * @date    30-September-2011
+  * @brief   CMSIS Cortex-M4 Device Peripheral Access Layer System Source File.
+  *          This file contains the system clock configuration for STM32F4xx devices,
+  *          and is generated by the clock configuration tool
+  *          stm32f4xx_Clock_Configuration_V1.0.0.xls
+  *             
+  * 1.  This file provides two functions and one global variable to be called from 
+  *     user application:
+  *      - SystemInit(): Setups the system clock (System clock source, PLL Multiplier
+  *                      and Divider factors, AHB/APBx prescalers and Flash settings),
+  *                      depending on the configuration made in the clock xls tool. 
+  *                      This function is called at startup just after reset and 
+  *                      before branch to main program. This call is made inside
+  *                      the "startup_stm32f4xx.s" file.
+  *
+  *      - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
+  *                                  by the user application to setup the SysTick 
+  *                                  timer or configure other parameters.
+  *                                     
+  *      - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
+  *                                 be called whenever the core clock is changed
+  *                                 during program execution.
+  *
+  * 2. After each device reset the HSI (16 MHz) is used as system clock source.
+  *    Then SystemInit() function is called, in "startup_stm32f4xx.s" file, to
+  *    configure the system clock before to branch to main program.
+  *
+  * 3. If the system clock source selected by user fails to startup, the SystemInit()
+  *    function will do nothing and HSI still used as system clock source. User can 
+  *    add some code to deal with this issue inside the SetSysClock() function.
+  *
+  * 4. The default value of HSE crystal is set to 25MHz, refer to "HSE_VALUE" define
+  *    in "stm32f4xx.h" file. When HSE is used as system clock source, directly or
+  *    through PLL, and you are using different crystal you have to adapt the HSE
+  *    value to your own configuration.
+  *
+  * 5. This file configures the system clock as follows:
+  *=============================================================================
+  *=============================================================================
+  *        Supported STM32F4xx device revision    | Rev A
+  *-----------------------------------------------------------------------------
+  *        System Clock source                    | PLL (HSE)
+  *-----------------------------------------------------------------------------
+  *        SYSCLK(Hz)                             | 168000000
+  *-----------------------------------------------------------------------------
+  *        HCLK(Hz)                               | 168000000
+  *-----------------------------------------------------------------------------
+  *        AHB Prescaler                          | 1
+  *-----------------------------------------------------------------------------
+  *        APB1 Prescaler                         | 4
+  *-----------------------------------------------------------------------------
+  *        APB2 Prescaler                         | 2
+  *-----------------------------------------------------------------------------
+  *        HSE Frequency(Hz)                      | 25000000
+  *-----------------------------------------------------------------------------
+  *        PLL_M                                  | 25
+  *-----------------------------------------------------------------------------
+  *        PLL_N                                  | 336
+  *-----------------------------------------------------------------------------
+  *        PLL_P                                  | 2
+  *-----------------------------------------------------------------------------
+  *        PLL_Q                                  | 7
+  *-----------------------------------------------------------------------------
+  *        PLLI2S_N                               | NA
+  *-----------------------------------------------------------------------------
+  *        PLLI2S_R                               | NA
+  *-----------------------------------------------------------------------------
+  *        I2S input clock                        | NA
+  *-----------------------------------------------------------------------------
+  *        VDD(V)                                 | 3.3
+  *-----------------------------------------------------------------------------
+  *        Main regulator output voltage          | Scale1 mode
+  *-----------------------------------------------------------------------------
+  *        Flash Latency(WS)                      | 5
+  *-----------------------------------------------------------------------------
+  *        Prefetch Buffer                        | OFF
+  *-----------------------------------------------------------------------------
+  *        Instruction cache                      | ON
+  *-----------------------------------------------------------------------------
+  *        Data cache                             | ON
+  *-----------------------------------------------------------------------------
+  *        Require 48MHz for USB OTG FS,          | Enabled
+  *        SDIO and RNG clock                     |
+  *-----------------------------------------------------------------------------
+  *=============================================================================
+  ****************************************************************************** 
+  * @attention
+  *
+  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
+  ******************************************************************************
+  */
+
+/** @addtogroup CMSIS
+  * @{
+  */
+
+/** @addtogroup stm32f4xx_system
+  * @{
+  */  
+  
+/** @addtogroup STM32F4xx_System_Private_Includes
+  * @{
+  */
+
+#include "stm32f4xx.h"
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_TypesDefinitions
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Defines
+  * @{
+  */
+
+/************************* Miscellaneous Configuration ************************/
+/*!< Uncomment the following line if you need to use external SRAM mounted
+     on STM324xG_EVAL board as data memory  */
+/* #define DATA_IN_ExtSRAM */
+
+/*!< Uncomment the following line if you need to relocate your vector Table in
+     Internal SRAM. */
+/* #define VECT_TAB_SRAM */
+#define VECT_TAB_OFFSET  0x00 /*!< Vector Table base offset field. 
+                                   This value must be a multiple of 0x200. */
+/******************************************************************************/
+
+/************************* PLL Parameters *************************************/
+/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
+#define PLL_M      25
+#define PLL_N      336
+
+/* SYSCLK = PLL_VCO / PLL_P */
+#define PLL_P      2
+
+/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */
+#define PLL_Q      7
+
+/******************************************************************************/
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Macros
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Variables
+  * @{
+  */
+
+  uint32_t SystemCoreClock = 168000000;
+
+  __I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes
+  * @{
+  */
+
+static void SetSysClock(void);
+#ifdef DATA_IN_ExtSRAM
+  static void SystemInit_ExtMemCtl(void); 
+#endif /* DATA_IN_ExtSRAM */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F4xx_System_Private_Functions
+  * @{
+  */
+
+/**
+  * @brief  Setup the microcontroller system
+  *         Initialize the Embedded Flash Interface, the PLL and update the 
+  *         SystemFrequency variable.
+  * @param  None
+  * @retval None
+  */
+void SystemInit(void)
+{
+  /* FPU settings ------------------------------------------------------------*/
+  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
+    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
+  #endif
+
+  /* Reset the RCC clock configuration to the default reset state ------------*/
+  /* Set HSION bit */
+  RCC->CR |= (uint32_t)0x00000001;
+
+  /* Reset CFGR register */
+  RCC->CFGR = 0x00000000;
+
+  /* Reset HSEON, CSSON and PLLON bits */
+  RCC->CR &= (uint32_t)0xFEF6FFFF;
+
+  /* Reset PLLCFGR register */
+  RCC->PLLCFGR = 0x24003010;
+
+  /* Reset HSEBYP bit */
+  RCC->CR &= (uint32_t)0xFFFBFFFF;
+
+  /* Disable all interrupts */
+  RCC->CIR = 0x00000000;
+
+#ifdef DATA_IN_ExtSRAM
+  SystemInit_ExtMemCtl(); 
+#endif /* DATA_IN_ExtSRAM */
+         
+  /* Configure the System clock source, PLL Multiplier and Divider factors, 
+     AHB/APBx prescalers and Flash settings ----------------------------------*/
+  SetSysClock();
+
+  /* Configure the Vector Table location add offset address ------------------*/
+#ifdef VECT_TAB_SRAM
+  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
+#else
+  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
+#endif
+}
+
+/**
+   * @brief  Update SystemCoreClock variable according to Clock Register Values.
+  *         The SystemCoreClock variable contains the core clock (HCLK), it can
+  *         be used by the user application to setup the SysTick timer or configure
+  *         other parameters.
+  *           
+  * @note   Each time the core clock (HCLK) changes, this function must be called
+  *         to update SystemCoreClock variable value. Otherwise, any configuration
+  *         based on this variable will be incorrect.         
+  *     
+  * @note   - The system frequency computed by this function is not the real 
+  *           frequency in the chip. It is calculated based on the predefined 
+  *           constant and the selected clock source:
+  *             
+  *           - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
+  *                                              
+  *           - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
+  *                          
+  *           - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) 
+  *             or HSI_VALUE(*) multiplied/divided by the PLL factors.
+  *         
+  *         (*) HSI_VALUE is a constant defined in stm32f4xx.h file (default value
+  *             16 MHz) but the real value may vary depending on the variations
+  *             in voltage and temperature.   
+  *    
+  *         (**) HSE_VALUE is a constant defined in stm32f4xx.h file (default value
+  *              25 MHz), user has to ensure that HSE_VALUE is same as the real
+  *              frequency of the crystal used. Otherwise, this function may
+  *              have wrong result.
+  *                
+  *         - The result of this function could be not correct when using fractional
+  *           value for HSE crystal.
+  *     
+  * @param  None
+  * @retval None
+  */
+void SystemCoreClockUpdate(void)
+{
+  uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;
+  
+  /* Get SYSCLK source -------------------------------------------------------*/
+  tmp = RCC->CFGR & RCC_CFGR_SWS;
+
+  switch (tmp)
+  {
+    case 0x00:  /* HSI used as system clock source */
+      SystemCoreClock = HSI_VALUE;
+      break;
+    case 0x04:  /* HSE used as system clock source */
+      SystemCoreClock = HSE_VALUE;
+      break;
+    case 0x08:  /* PLL used as system clock source */
+
+      /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
+         SYSCLK = PLL_VCO / PLL_P
+         */    
+      pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
+      pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
+      
+      if (pllsource != 0)
+      {
+        /* HSE used as PLL clock source */
+        pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
+      }
+      else
+      {
+        /* HSI used as PLL clock source */
+        pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);      
+      }
+
+      pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;
+      SystemCoreClock = pllvco/pllp;
+      break;
+    default:
+      SystemCoreClock = HSI_VALUE;
+      break;
+  }
+  /* Compute HCLK frequency --------------------------------------------------*/
+  /* Get HCLK prescaler */
+  tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
+  /* HCLK frequency */
+  SystemCoreClock >>= tmp;
+}
+
+/**
+  * @brief  Configures the System clock source, PLL Multiplier and Divider factors, 
+  *         AHB/APBx prescalers and Flash settings
+  * @Note   This function should be called only once the RCC clock configuration  
+  *         is reset to the default reset state (done in SystemInit() function).   
+  * @param  None
+  * @retval None
+  */
+static void SetSysClock(void)
+{
+/******************************************************************************/
+/*            PLL (clocked by HSE) used as System clock source                */
+/******************************************************************************/
+  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+  
+  /* Enable HSE */
+  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
+  /* Wait till HSE is ready and if Time out is reached exit */
+  do
+  {
+    HSEStatus = RCC->CR & RCC_CR_HSERDY;
+    StartUpCounter++;
+  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
+  {
+    HSEStatus = (uint32_t)0x01;
+  }
+  else
+  {
+    HSEStatus = (uint32_t)0x00;
+  }
+
+  if (HSEStatus == (uint32_t)0x01)
+  {
+    /* Select regulator voltage output Scale 1 mode, System frequency up to 168 MHz */
+    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
+    PWR->CR |= PWR_CR_VOS;
+
+    /* HCLK = SYSCLK / 1*/
+    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
+      
+    /* PCLK2 = HCLK / 2*/
+    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
+    
+    /* PCLK1 = HCLK / 4*/
+    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
+
+    /* Configure the main PLL */
+    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
+                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
+
+    /* Enable the main PLL */
+    RCC->CR |= RCC_CR_PLLON;
+
+    /* Wait till the main PLL is ready */
+    while((RCC->CR & RCC_CR_PLLRDY) == 0)
+    {
+    }
+   
+    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
+    FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
+
+    /* Select the main PLL as system clock source */
+    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
+    RCC->CFGR |= RCC_CFGR_SW_PLL;
+
+    /* Wait till the main PLL is used as system clock source */
+    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
+    {
+    }
+  }
+  else
+  { /* If HSE fails to start-up, the application will have wrong clock
+         configuration. User can add here some code to deal with this error */
+  }
+
+}
+
+/**
+  * @brief  Setup the external memory controller. Called in startup_stm32f4xx.s 
+  *          before jump to __main
+  * @param  None
+  * @retval None
+  */ 
+#ifdef DATA_IN_ExtSRAM
+/**
+  * @brief  Setup the external memory controller.
+  *         Called in startup_stm32f4xx.s before jump to main.
+  *         This function configures the external SRAM mounted on STM324xG_EVAL board
+  *         This SRAM will be used as program data memory (including heap and stack).
+  * @param  None
+  * @retval None
+  */
+void SystemInit_ExtMemCtl(void)
+{
+/*-- GPIOs Configuration -----------------------------------------------------*/
+/*
+ +-------------------+--------------------+------------------+------------------+
+ +                       SRAM pins assignment                                   +
+ +-------------------+--------------------+------------------+------------------+
+ | PD0  <-> FSMC_D2  | PE0  <-> FSMC_NBL0 | PF0  <-> FSMC_A0 | PG0 <-> FSMC_A10 | 
+ | PD1  <-> FSMC_D3  | PE1  <-> FSMC_NBL1 | PF1  <-> FSMC_A1 | PG1 <-> FSMC_A11 | 
+ | PD4  <-> FSMC_NOE | PE3  <-> FSMC_A19  | PF2  <-> FSMC_A2 | PG2 <-> FSMC_A12 | 
+ | PD5  <-> FSMC_NWE | PE4  <-> FSMC_A20  | PF3  <-> FSMC_A3 | PG3 <-> FSMC_A13 | 
+ | PD8  <-> FSMC_D13 | PE7  <-> FSMC_D4   | PF4  <-> FSMC_A4 | PG4 <-> FSMC_A14 | 
+ | PD9  <-> FSMC_D14 | PE8  <-> FSMC_D5   | PF5  <-> FSMC_A5 | PG5 <-> FSMC_A15 | 
+ | PD10 <-> FSMC_D15 | PE9  <-> FSMC_D6   | PF12 <-> FSMC_A6 | PG9 <-> FSMC_NE2 | 
+ | PD11 <-> FSMC_A16 | PE10 <-> FSMC_D7   | PF13 <-> FSMC_A7 |------------------+
+ | PD12 <-> FSMC_A17 | PE11 <-> FSMC_D8   | PF14 <-> FSMC_A8 | 
+ | PD13 <-> FSMC_A18 | PE12 <-> FSMC_D9   | PF15 <-> FSMC_A9 | 
+ | PD14 <-> FSMC_D0  | PE13 <-> FSMC_D10  |------------------+
+ | PD15 <-> FSMC_D1  | PE14 <-> FSMC_D11  |
+ |                   | PE15 <-> FSMC_D12  |
+ +-------------------+--------------------+
+*/
+   /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */
+  RCC->AHB1ENR   = 0x00000078;
+  
+  /* Connect PDx pins to FSMC Alternate function */
+  GPIOD->AFR[0]  = 0x00cc00cc;
+  GPIOD->AFR[1]  = 0xcc0ccccc;
+  /* Configure PDx pins in Alternate function mode */  
+  GPIOD->MODER   = 0xaaaa0a0a;
+  /* Configure PDx pins speed to 100 MHz */  
+  GPIOD->OSPEEDR = 0xffff0f0f;
+  /* Configure PDx pins Output type to push-pull */  
+  GPIOD->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PDx pins */ 
+  GPIOD->PUPDR   = 0x00000000;
+
+  /* Connect PEx pins to FSMC Alternate function */
+  GPIOE->AFR[0]  = 0xc00cc0cc;
+  GPIOE->AFR[1]  = 0xcccccccc;
+  /* Configure PEx pins in Alternate function mode */ 
+  GPIOE->MODER   = 0xaaaa828a;
+  /* Configure PEx pins speed to 100 MHz */ 
+  GPIOE->OSPEEDR = 0xffffc3cf;
+  /* Configure PEx pins Output type to push-pull */  
+  GPIOE->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PEx pins */ 
+  GPIOE->PUPDR   = 0x00000000;
+
+  /* Connect PFx pins to FSMC Alternate function */
+  GPIOF->AFR[0]  = 0x00cccccc;
+  GPIOF->AFR[1]  = 0xcccc0000;
+  /* Configure PFx pins in Alternate function mode */   
+  GPIOF->MODER   = 0xaa000aaa;
+  /* Configure PFx pins speed to 100 MHz */ 
+  GPIOF->OSPEEDR = 0xff000fff;
+  /* Configure PFx pins Output type to push-pull */  
+  GPIOF->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PFx pins */ 
+  GPIOF->PUPDR   = 0x00000000;
+
+  /* Connect PGx pins to FSMC Alternate function */
+  GPIOG->AFR[0]  = 0x00cccccc;
+  GPIOG->AFR[1]  = 0x000000c0;
+  /* Configure PGx pins in Alternate function mode */ 
+  GPIOG->MODER   = 0x00080aaa;
+  /* Configure PGx pins speed to 100 MHz */ 
+  GPIOG->OSPEEDR = 0x000c0fff;
+  /* Configure PGx pins Output type to push-pull */  
+  GPIOG->OTYPER  = 0x00000000;
+  /* No pull-up, pull-down for PGx pins */ 
+  GPIOG->PUPDR   = 0x00000000;
+  
+/*-- FSMC Configuration ------------------------------------------------------*/
+  /* Enable the FSMC interface clock */
+  RCC->AHB3ENR         = 0x00000001;
+
+  /* Configure and enable Bank1_SRAM2 */
+  FSMC_Bank1->BTCR[2]  = 0x00001015;
+  FSMC_Bank1->BTCR[3]  = 0x00010603;
+  FSMC_Bank1E->BWTR[2] = 0x0fffffff;
+/*
+  Bank1_SRAM2 is configured as follow:
+
+  p.FSMC_AddressSetupTime = 3;
+  p.FSMC_AddressHoldTime = 0;
+  p.FSMC_DataSetupTime = 6;
+  p.FSMC_BusTurnAroundDuration = 1;
+  p.FSMC_CLKDivision = 0;
+  p.FSMC_DataLatency = 0;
+  p.FSMC_AccessMode = FSMC_AccessMode_A;
+
+  FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2;
+  FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_PSRAM;
+  FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
+  FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;  
+  FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
+  FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
+  FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
+  FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
+  FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;
+  FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;
+*/
+  
+}
+#endif /* DATA_IN_ExtSRAM */
+
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+  
+/**
+  * @}
+  */    
+/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
diff --git a/examples/test-uart/uart.c b/examples/test-uart/uart.c
new file mode 100644 (file)
index 0000000..1567135
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(__ARM_EABI__)
+#include "stm32f4xx.h"
+#elif defined(__AVR__)
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <ucg_irq.h>
+#include <ucg_cirbuf.h>
+#include <ucg_uart.h>
+
+#include "uart.h"
+
+/* rx & tx buffers */
+static char rx_buf[64];
+static struct ucg_cirbuf rx_cirbuf;
+static char tx_buf[64];
+static struct ucg_cirbuf tx_cirbuf;
+/* generic uart struct */
+struct ucg_uart main_uart;
+
+#if defined(__ARM_EABI__)
+
+#include <ucg_gloss_chardev.h>
+#include <ucg_stm32_uart.h>
+
+static struct ucg_stm32_uart stm32_uart_data = {
+       .uart = USART2,
+       .rcc_uart = RCC_APB1Periph_USART2,
+       .rcc_gpio = RCC_AHB1Periph_GPIOA,
+       .gpio = GPIOA,
+       .gpio_af = GPIO_AF_USART2,
+       .gpio_pins = GPIO_Pin_2 | GPIO_Pin_3,
+       .gpio_speed = GPIO_Speed_25MHz,
+       .irq = USART2_IRQn,
+       .irq_preempt_prio = 2,
+       .irq_sub_prio = 0,
+};
+
+static _ssize_t
+uart_write_r(__attribute__((unused)) struct _reent *r,
+       __attribute__((unused)) int fd,
+       const void *ptr, size_t len)
+{
+       size_t i;
+       const char *buf = ptr;
+
+       for (i = 0; i < len; i++)
+               ucg_uart_send(&main_uart, buf[i], WAIT);
+
+       return len;
+}
+
+static _ssize_t
+uart_read_r(__attribute__((unused)) struct _reent *r,
+       __attribute__((unused)) int fd,
+       void *ptr, size_t len)
+{
+       int c;
+       char *buf = ptr;
+
+       c = ucg_uart_recv(&main_uart, NOWAIT);
+       if (c < 0) {
+               r->_errno = EAGAIN;
+               return 0;
+       }
+       buf[0] = c;
+       return 1;
+}
+
+void USART2_IRQHandler(void)
+{
+       if ((USART2->SR & USART_FLAG_TXE))
+               ucg_uart_tx_intr(&main_uart);
+       if ((USART2->SR & USART_FLAG_RXNE))
+               ucg_uart_rx_intr(&main_uart);
+}
+
+struct ucg_chardev uart_stdin_dev = {
+       .name = "stdin",
+       .open_r = NULL,
+       .close_r = NULL,
+       .read_r = uart_read_r,
+       .write_r = NULL,
+};
+
+struct ucg_chardev uart_stdout_dev = {
+       .name = "stdout",
+       .open_r = NULL,
+       .close_r = NULL,
+       .read_r = NULL,
+       .write_r = uart_write_r,
+};
+
+struct ucg_chardev uart_stderr_dev = {
+       .name = "stderr",
+       .open_r = NULL,
+       .close_r = NULL,
+       .read_r = NULL,
+       .write_r = uart_write_r,
+};
+
+static void uart_register_stdio(void)
+{
+       ucg_chardev_register(&uart_stdin_dev);
+       ucg_chardev_register(&uart_stdout_dev);
+       ucg_chardev_register(&uart_stderr_dev);
+
+       /* Disable buffering */
+       setbuf(stdin, NULL);
+       setbuf(stdout, NULL);
+       setbuf(stderr, NULL);
+}
+
+#elif defined(__AVR__)
+
+#include <ucg_avr_uart.h>
+/* avr-specific uart struct */
+static struct ucg_avr_uart avr_uart_data = {
+       .reg_udr = &UDR0,
+       .reg_ucsra = &UCSR0A,
+       .reg_ucsrb = &UCSR0B,
+       .reg_ucsrc = &UCSR0C,
+       .reg_ubrrl = &UBRR0L,
+       .reg_ubrrh = &UBRR0H,
+       .bit_udre = UDRE0,
+       .bit_rxc = RXC0,
+       .bit_udrie = UDRIE0,
+       .bit_rxen = RXEN0,
+       .bit_txen = TXEN0,
+       .bit_rxcie = RXCIE0,
+       .bit_u2x = U2X0,
+};
+
+/* send on stdout */
+static int std_send(char c, FILE *f)
+{
+       (void)f;
+       ucg_uart_send(&main_uart, c, WAIT);
+       return 0;
+}
+
+/* recv on stdin */
+static int std_recv(FILE *f)
+{
+       int16_t c;
+
+       (void)f;
+       c = ucg_uart_recv(&main_uart, NOWAIT);
+       if (c < 0)
+               return _FDEV_EOF;
+
+       return c;
+}
+
+SIGNAL(USART_RX_vect)
+{
+       ucg_uart_rx_intr(&main_uart);
+}
+
+SIGNAL(USART_UDRE_vect)
+{
+       ucg_uart_tx_intr(&main_uart);
+}
+#endif
+
+int uart_init(void)
+{
+       int ret;
+       struct ucg_uart_config conf;
+       const void *uart_ops;
+       void *uart_data;
+
+#if defined(__ARM_EABI__)
+       uart_ops = &stm32_uart_ops;
+       uart_data = &stm32_uart_data;
+#else
+       uart_ops = &avr_uart_ops;
+       uart_data = &avr_uart_data;
+#endif
+
+       ret = ucg_uart_init(&main_uart, uart_ops, uart_data,
+               &rx_cirbuf, rx_buf, sizeof(rx_buf),
+               &tx_cirbuf, tx_buf, sizeof(tx_buf));
+       if (ret < 0)
+               return ret;
+
+       ucg_uart_getconf(&main_uart, &conf);
+       conf.baudrate = 57600;
+       ret = ucg_uart_setconf(&main_uart, &conf);
+       if (ret < 0)
+               return ret;
+
+#if defined(__ARM_EABI__)
+       uart_register_stdio();
+#elif defined(__AVR__)
+       fdevopen(std_send, std_recv);
+#endif
+       return ret;
+}
diff --git a/examples/test-uart/uart.h b/examples/test-uart/uart.h
new file mode 100644 (file)
index 0000000..ca1a139
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UART_H_
+#define UART_H_
+
+#include <stdio.h>
+
+int uart_init(void);
+
+#endif
diff --git a/lib/callout/include/ucg_callout.h b/lib/callout/include/ucg_callout.h
new file mode 100644 (file)
index 0000000..34bb29d
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) <2014-2015>, Olivier Matz <zer0@droids-corp.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Inspired from Intel DPDK rte_timer library */
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_CALLOUT_H_
+#define UCG_CALLOUT_H_
+
+#include <sys/queue.h>
+
+#define UCG_CALLOUT_STATS
+/* #define UCG_CALLOUT_DEBUG */
+
+/**
+ * This module provides a timer service. The manager function
+ * ucg_callout_manage() can be called from an interrupt or from a
+ * standard function (usually a main-loop). In the latter case, no
+ * preemption is possible.
+ *
+ * Each timer has a priority: the timers with higher priorities are
+ * scheduled before the others. This feature is mostly useful when the
+ * manager is called from an interrupt. Indeed, the callback function of
+ * a timer with a high priority cannot be preempted by a timer with a
+ * lower priority.
+ *
+ * The module locks interrupts when doing critical operations, ensuring that
+ * critical data are accessed atomically.
+ *
+ * State of timers:
+ * - stopped: initial state after ucg_callout_init()
+ * - scheduled: after a call to ucg_callout_schedule(), the timer is in the
+ *   scheduled list of the callout manager
+ * - expired: after a call to ucg_callout_manage(), if the expire time of a
+ *   timer is reached, it is moved in a local list and its state is
+ *   changed to "expired".
+ * - before starting the callback, the timer goes in state "running".
+ *
+ * Once running, the associated timer is not touched anymore by
+ * ucg_callout_manage(). As a result, the timer MUST be either reloaded
+ * or stopped (and potentially freed).
+ */
+
+/**
+ * Maximum number of nested preemptions.
+ */
+#define UCG_CALLOUT_MAX_RECURSION 5
+
+#ifdef UCG_CALLOUT_STATS
+/**
+ * The structure that stores the timer statistics, mostly useful for debug
+ * purposes.
+ */
+struct ucg_callout_debug_stats {
+       uint32_t schedule;      /**< nb of calls to ucg_callout_(re)schedule() */
+       uint32_t stop;          /**< nb of calls to ucg_callout_stop() */
+       uint32_t manage;        /**< nb of calls to ucg_callout_manage() */
+       uint32_t max_recursion; /** manage() skipped due to max recursion */
+       uint32_t delayed;       /** task delayed a bit due to low prio */
+       uint32_t hard_delayed;  /** task recheduled later due to low priority */
+
+       uint8_t cur_scheduled;  /**< current number of scheduled timers */
+       uint8_t cur_expired;    /**< current number of expired timers */
+       uint8_t cur_running;    /**< current number of running timers */
+};
+#endif
+
+struct ucg_callout;
+struct ucg_callout_mgr;
+
+/**
+ * The type of a callout callback function.
+ */
+typedef void (ucg_callout_cb_t)(struct ucg_callout_mgr *cm,
+       struct ucg_callout *tim, void *arg);
+
+/**
+ * A callout structure, storing all data associated to a timer.
+ */
+struct ucg_callout {
+       LIST_ENTRY(ucg_callout) next; /**< next/prev in list */
+
+#define UCG_CALLOUT_STATE_STOPPED    0 /**< not scheduled */
+#define UCG_CALLOUT_STATE_SCHEDULED  1 /**< in the scheduled list */
+#define UCG_CALLOUT_STATE_EXPIRED    2 /**< expired, will be executed soon */
+#define UCG_CALLOUT_STATE_RUNNING    3 /**< being executed */
+       uint8_t state;             /**< stopped, scheduled, expired */
+       uint8_t priority;          /**< the priority of the timer */
+       uint16_t expire;           /**< time when timer should expire */
+
+       ucg_callout_cb_t *f;       /**< callback function pointer */
+       void *arg;                 /**< argument given to the cb function. */
+};
+
+/* define the callout list */
+LIST_HEAD(ucg_callout_list, ucg_callout);
+
+/* static initializer for a timer structure */
+#define UCG_CALLOUT_INITIALIZER { }
+
+/**
+ * Type of the function used by a callout manager to get a time reference
+ */
+typedef uint16_t (ucg_callout_get_time_t)(void);
+
+/**
+ * An instance of callout manager. It is possible to have several
+ * managers. A callout is attached to one manager at a time.
+ */
+struct ucg_callout_mgr {
+       ucg_callout_get_time_t *get_time; /**< func to get the time reference */
+       uint16_t prev_time;   /**< time of previous call */
+       uint8_t cur_priority; /** priority of running event */
+       uint8_t nb_recursion; /** number of recursion */
+       struct ucg_callout_list sched_list; /**< list of scheduled timers */
+
+#ifdef UCG_CALLOUT_STATS
+       struct ucg_callout_debug_stats stats; /**< stats */
+#endif
+};
+
+/**
+ * Initialize a callout manager
+ *
+ * The callout manager must be initialized before ucg_callout_add() or
+ * ucg_callout_manage() can be called.
+ *
+ * @param cm
+ *   Pointer to the uninitialized callout manager structure.
+ * @param get_time
+ *   Pointer to a function that returns a time reference (unsigned 16 bits).
+ */
+void ucg_callout_mgr_init(struct ucg_callout_mgr *cm,
+       ucg_callout_get_time_t *get_time);
+
+/**
+ * Initialize a callout structure and set callback function
+ *
+ * Before doing any operation on the callout structure, it has to be
+ * initialized with this function. It is possible to reinitialize a
+ * timer that has been previously scheduled, but it must be stopped.
+ *
+ * @param tim
+ *   The timer to initialize.
+ * @param priority
+ *   The priority of the callout (high value means higher priority)
+ * @param f
+ *   The callback function of the timer.
+ * @param arg
+ *   The user argument of the callback function.
+ */
+void ucg_callout_init(struct ucg_callout *tim, ucg_callout_cb_t f, void *arg,
+       uint8_t priority);
+
+/**
+ * Schedule a callout
+ *
+ * The ucg_callout_schedule() function adds the timer in the scheduled
+ * list. After the specified amount of ticks are elapsed, the callback
+ * function of the timer previously given to ucg_callout_init() will be
+ * invoked with its argument.
+ *
+ * The given "tick" value is relative to the current time, and is 16 bits
+ * wide. As it internally uses signed 16 bits comparison, the max value for
+ * ticks is 32767.
+ *
+ * @param cm
+ *   The callout manager where the timer should be scheduled
+ * @param tim
+ *   The timer handle
+ * @param ticks
+ *   The number of ticks before the callback function is called, relative to now
+ *   (the reference is given by the get_time() function of the callout manager).
+ * @return
+ *   0 on success, negative on error
+ */
+int ucg_callout_schedule(struct ucg_callout_mgr *cm, struct ucg_callout *tim,
+       uint16_t ticks);
+
+/**
+ * Reschedule a callout
+ *
+ * This function does exactly the same than ucg_callout_schedule()
+ * except that the given time "ticks" is not relative to the current
+ * time but to the "expire" field of the timer.
+ *
+ * Using this function is advised to avoid drift if you want to have periodic
+ * timers.
+ *
+ * This function should preferably be called from the callback function of
+ * the timer. Indeed, if the "expire" field should be a known value or it
+ * can result in an undefined behavior
+ *
+ * The given "tick" value is relative to the "expire" field of the
+ * timer, and is 16 bits wide. As it internally uses signed 16 bits
+ * comparison, the max value for ticks is 32767.
+ *
+ * @param cm
+ *   The callout manager where the timer should be scheduled
+ * @param tim
+ *   The timer handle
+ * @param ticks
+ *   The number of ticks before the callback function is called, relative to
+ *   the "expire" value of the timer
+ * @return
+ *   0 on success, negative on error
+ */
+int ucg_callout_reschedule(struct ucg_callout_mgr *cm, struct ucg_callout *tim,
+       uint16_t ticks);
+
+/**
+ * Stop a timer.
+ *
+ * The ucg_callout_stop() function stops a timer associated with the
+ * timer handle tim.
+ *
+ * If the timer is scheduled or expired, it is removed from the list:
+ * the callback function won't be invoked. If the timer is stopped or
+ * running the function does nothing.
+ *
+ * If a timer structure is dynamically allocated, invoking
+ * ucg_callout_stop() is needed before freeing the structure, even if
+ * the freeing occurs in a callback. Indeed, this function can be called
+ * safely from a timer callback. If it succeeds, the timer is not
+ * referenced anymore by the callout manager.
+ *
+ * @param cm
+ *   The callout manager where the timer is or was scheduled
+ * @param tim
+ *   The timer
+ * @return
+ *   0 on success, negative on error
+ */
+void ucg_callout_stop(struct ucg_callout_mgr *cm, struct ucg_callout *tim);
+
+/**
+ * Return the state of a timer
+ *
+ * @param tim
+ *   The timer
+ * @return
+ *   - UCG_CALLOUT_STATE_STOPPED: the timer is stopped
+ *   - UCG_CALLOUT_STATE_SCHEDULED: the timer is scheduled
+ *   - UCG_CALLOUT_STATE_EXPIRED: the timer was moved in a local list before
+ *     execution
+ */
+static inline uint8_t ucg_callout_state(struct ucg_callout *tim)
+{
+       return tim->state;
+}
+
+/**
+ * Manage the timer list and execute callback functions.
+ *
+ * This function must be called periodically, either from a loop of from
+ * an interrupt. It browses the list of scheduled timers and runs all
+ * timers that are expired.
+ *
+ * This function must be called at least every 16384 reference ticks of
+ * cm->get_time(), but calling it more often is recommanded to avoid
+ * delaying task abusively.
+ *
+ * The function must be called with IRQ allowed.
+ */
+void ucg_callout_manage(struct ucg_callout_mgr *cm);
+
+/**
+ * Dump statistics about timers.
+ */
+void ucg_callout_dump_stats(struct ucg_callout_mgr *cm);
+
+/**
+ * Set the current priority level
+ *
+ * Prevent callout with a priority lower than "new_prio" to be executed.
+ * If the current priority of the callout manager is already lower higher
+ * than "new_prio", the function won't change the running priority.
+ *
+ * The returned value should be stored by the caller and restored with
+ * ucg_callout_mgr_restore_prio(), preferably in the same function.
+ *
+ * @param cm
+ *   The callout manager
+ * @param new_prio
+ *   The new running priority
+ *
+ * @return
+ *   The value of the running priority before the call og this function
+ */
+uint8_t ucg_callout_mgr_set_prio(struct ucg_callout_mgr *cm, uint8_t new_prio);
+
+/**
+ * Restore the current priority level
+ *
+ * Used after a call to ucg_callout_mgr_set_prio().
+ *
+ * @param cm
+ *   The callout manager
+ * @param old_prio
+ *   The old running priority
+ */
+void ucg_callout_mgr_restore_prio(struct ucg_callout_mgr *cm, uint8_t old_prio);
+
+#endif /* UCG_CALLOUT_H_ */
diff --git a/lib/callout/ucg_callout.c b/lib/callout/ucg_callout.c
new file mode 100644 (file)
index 0000000..8e51240
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) <2014-2015>, Olivier Matz <zer0@droids-corp.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Inspired from Intel DPDK rte_timer library */
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/queue.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <ucg_irq.h>
+#include <ucg_callout.h>
+
+/* allow to browse a list while modifying the current element */
+#define _LIST_FOREACH_SAFE(cur, next, head, field)                     \
+       for ((cur) = LIST_FIRST((head)),                                \
+                    (next) = ((cur) ? LIST_NEXT((cur), field) : NULL); \
+            (cur);                                                     \
+            (cur) = (next),                                            \
+                    (next) = ((cur) ? LIST_NEXT((cur), field) : NULL))
+
+#ifdef UCG_CALLOUT_STATS
+/* called with irq locked */
+#define CALLOUT_STAT_ADD(cm, field, x) do {    \
+       cm->stats.field += x;                   \
+       } while(0)
+#else
+#define CALLOUT_STAT_ADD(cm, field, x) do { } while(0)
+#endif
+
+#ifdef UCG_CALLOUT_DEBUG
+#define callout_dprintf(fmt, ...) \
+       printf("%s(): " fmt, __FUNCTION__, __VA_ARGS__)
+#else
+#define callout_dprintf(...) do { } while (0)
+#endif
+
+/* Initialize a callout manager */
+void
+ucg_callout_mgr_init(struct ucg_callout_mgr *cm,
+       ucg_callout_get_time_t *get_time)
+{
+       memset(cm, 0, sizeof(*cm));
+       cm->get_time = get_time;
+       LIST_INIT(&cm->sched_list);
+}
+
+/* Initialize the timer handle tim for use */
+void
+ucg_callout_init(struct ucg_callout *tim, ucg_callout_cb_t f, void *arg,
+       uint8_t priority)
+{
+       memset(tim, 0, sizeof(*tim));
+       tim->f = f;
+       tim->arg = arg;
+       tim->priority = priority;
+}
+
+/*
+ * Add a timer in the scheduled list (timer must not already be in a list). The
+ * timers are sorted in the list according the expire time (the closer timers
+ * first).
+ *
+ * called with irq locked
+ */
+static void
+callout_add_in_sched_list(struct ucg_callout_mgr *cm, struct ucg_callout *tim)
+{
+       struct ucg_callout *t, *prev_t;
+
+       callout_dprintf("cm=%p tim=%p\r\n", cm, tim);
+       tim->state = UCG_CALLOUT_STATE_SCHEDULED;
+
+       /* list is empty */
+       if (LIST_EMPTY(&cm->sched_list)) {
+               LIST_INSERT_HEAD(&cm->sched_list, tim, next);
+               return;
+       }
+
+       /* 'tim' expires before first entry */
+       t = LIST_FIRST(&cm->sched_list);
+       if ((int16_t)(tim->expire - t->expire) <= 0) {
+               LIST_INSERT_HEAD(&cm->sched_list, tim, next);
+               return;
+       }
+
+       /* find an element that will expire after 'tim' */
+       LIST_FOREACH(t, &cm->sched_list, next) {
+               if ((int16_t)(tim->expire - t->expire) <= 0) {
+                       LIST_INSERT_BEFORE(t, tim, next);
+                       return;
+               }
+               prev_t = t;
+       }
+
+       /* not found, insert at the end of the list */
+       LIST_INSERT_AFTER(prev_t, tim, next);
+}
+
+/*
+ * Add a timer in the local expired list (timer must not already be in a
+ * list). The timers are sorted in the list according to the priority (high
+ * priority first).
+ *
+ * called with irq locked
+ */
+static void
+callout_add_in_expired_list(struct ucg_callout_mgr *cm,
+       struct ucg_callout_list *expired_list, struct ucg_callout *tim)
+{
+       struct ucg_callout *t, *prev_t;
+
+       (void)cm; /* avoid warning if debug is disabled */
+
+       callout_dprintf("cm=%p tim=%p\r\n", cm, tim);
+       tim->state = UCG_CALLOUT_STATE_EXPIRED;
+
+       /* list is empty */
+       if (LIST_EMPTY(expired_list)) {
+               LIST_INSERT_HEAD(expired_list, tim, next);
+               return;
+       }
+
+       /* 'tim' has a higher prio */
+       t = LIST_FIRST(expired_list);
+       if (tim->priority >= t->priority) {
+               LIST_INSERT_HEAD(expired_list, tim, next);
+               return;
+       }
+
+       /* find an element that will expire after 'tim' */
+       LIST_FOREACH(t, expired_list, next) {
+               if (tim->priority >= t->priority) {
+                       LIST_INSERT_BEFORE(t, tim, next);
+                       return;
+               }
+               prev_t = t;
+       }
+
+       /* not found, insert at the end of the list */
+       LIST_INSERT_AFTER(prev_t, tim, next);
+}
+
+/*
+ * del from list (timer must be in a list)
+ */
+static void
+callout_del(struct ucg_callout_mgr *cm, struct ucg_callout *tim)
+{
+       (void)cm; /* avoid warning if debug is disabled */
+       callout_dprintf("cm=%p tim=%p\r\n", cm, tim);
+       LIST_REMOVE(tim, next);
+}
+
+/* Reset and start the timer associated with the timer handle tim */
+static int
+__callout_schedule(struct ucg_callout_mgr *cm, struct ucg_callout *tim,
+       uint16_t expire)
+{
+       ucg_irqflags_t flags;
+
+       callout_dprintf("cm=%p tim=%p expire=%d\r\n",
+                         cm, tim, expire);
+
+       flags = ucg_irq_lock_save();
+       CALLOUT_STAT_ADD(cm, schedule, 1);
+
+       /* remove it from list */
+       if (tim->state != UCG_CALLOUT_STATE_STOPPED) {
+               /* stats */
+               if (tim->state == UCG_CALLOUT_STATE_SCHEDULED)
+                       CALLOUT_STAT_ADD(cm, cur_scheduled, -1);
+               else if (tim->state == UCG_CALLOUT_STATE_EXPIRED)
+                       CALLOUT_STAT_ADD(cm, cur_expired, -1);
+               if (tim->state == UCG_CALLOUT_STATE_RUNNING)
+                       CALLOUT_STAT_ADD(cm, cur_running, -1);
+
+               callout_del(cm, tim);
+       }
+
+       tim->expire = expire;
+       CALLOUT_STAT_ADD(cm, cur_scheduled, 1);
+       callout_add_in_sched_list(cm, tim);
+       ucg_irq_unlock_restore(flags);
+
+       return 0;
+}
+
+/* Reset and start the timer associated with the timer handle tim */
+int
+ucg_callout_schedule(struct ucg_callout_mgr *cm, struct ucg_callout *tim,
+       uint16_t ticks)
+{
+       return __callout_schedule(cm, tim, cm->get_time() + ticks);
+}
+
+/* Reset and start the timer associated with the timer handle tim */
+int
+ucg_callout_reschedule(struct ucg_callout_mgr *cm, struct ucg_callout *tim,
+       uint16_t ticks)
+{
+       return __callout_schedule(cm, tim, tim->expire + ticks);
+}
+
+/* Stop the timer associated with the timer handle tim */
+void
+ucg_callout_stop(struct ucg_callout_mgr *cm, struct ucg_callout *tim)
+{
+       ucg_irqflags_t flags;
+
+       callout_dprintf("cm=%p tim=%p\r\n", cm, tim);
+
+       flags = ucg_irq_lock_save();
+       if (tim->state != UCG_CALLOUT_STATE_STOPPED) {
+
+               /* stats */
+               if (tim->state == UCG_CALLOUT_STATE_SCHEDULED)
+                       CALLOUT_STAT_ADD(cm, cur_scheduled, -1);
+               else if (tim->state == UCG_CALLOUT_STATE_EXPIRED)
+                       CALLOUT_STAT_ADD(cm, cur_expired, -1);
+               if (tim->state == UCG_CALLOUT_STATE_RUNNING)
+                       CALLOUT_STAT_ADD(cm, cur_running, -1);
+               CALLOUT_STAT_ADD(cm, stop, 1);
+
+               /* remove it from list */
+               callout_del(cm, tim);
+               tim->state = UCG_CALLOUT_STATE_STOPPED;
+       }
+       ucg_irq_unlock_restore(flags);
+}
+
+/* must be called periodically, run all timer that expired */
+void ucg_callout_manage(struct ucg_callout_mgr *cm)
+{
+       struct ucg_callout_list expired_list;
+       struct ucg_callout_list reschedule_list;
+       struct ucg_callout *tim, *tim_next;
+       uint16_t cur_time;
+       uint8_t old_prio;
+       int16_t diff;
+
+       CALLOUT_STAT_ADD(cm, manage, 1);
+       callout_dprintf("cm=%p\r\n", cm);
+
+       /* maximize the number of self-recursions */
+       if (cm->nb_recursion >= UCG_CALLOUT_MAX_RECURSION) {
+               CALLOUT_STAT_ADD(cm, max_recursion, 1);
+               return;
+       }
+
+       ucg_irq_lock();
+       cm->nb_recursion++;
+       LIST_INIT(&expired_list);
+       LIST_INIT(&reschedule_list);
+       cur_time = cm->get_time();
+       old_prio = cm->cur_priority;
+
+       /* move all expired timers in a local list */
+       _LIST_FOREACH_SAFE(tim, tim_next, &cm->sched_list, next) {
+
+               diff = cur_time - tim->expire;
+
+               /* check the expiration time (tasks are sorted) */
+               if (diff < 0)
+                       break;
+
+               callout_dprintf("cm=%p diff=%d\r\n", cm, diff);
+
+               /* check the priority, if it's too low, inc stats */
+               if (tim->priority <= cm->cur_priority) {
+                       if (diff < 16484)
+                               CALLOUT_STAT_ADD(cm, delayed, 1);
+                       else {
+                               /* reschedule to avoid an overflow */
+                               CALLOUT_STAT_ADD(cm, hard_delayed, 1);
+                               LIST_REMOVE(tim, next);
+                               tim->expire = cur_time;
+                               LIST_INSERT_HEAD(&reschedule_list, tim, next);
+                       }
+                       continue;
+               }
+
+               LIST_REMOVE(tim, next);
+               callout_add_in_expired_list(cm, &expired_list, tim);
+               CALLOUT_STAT_ADD(cm, cur_scheduled, -1);
+               CALLOUT_STAT_ADD(cm, cur_expired, 1);
+       }
+
+       /* reschedule hard_delayed timers, this does not happen usually */
+       while (!LIST_EMPTY(&reschedule_list)) {
+               tim = LIST_FIRST(&reschedule_list);
+               LIST_REMOVE(tim, next);
+               callout_add_in_sched_list(cm, tim);
+       }
+
+       /* for each timer of 'expired' list, execute callback */
+       while (!LIST_EMPTY(&expired_list)) {
+               tim = LIST_FIRST(&expired_list);
+               LIST_REMOVE(tim, next);
+
+               /* execute callback function */
+               CALLOUT_STAT_ADD(cm, cur_expired, -1);
+               CALLOUT_STAT_ADD(cm, cur_running, 1);
+               tim->state = UCG_CALLOUT_STATE_RUNNING;
+               cm->cur_priority = tim->priority;
+               ucg_irq_unlock();
+               tim->f(cm, tim, tim->arg);
+               ucg_irq_lock();
+       }
+
+       cm->cur_priority = old_prio;
+       cm->nb_recursion--;
+       ucg_irq_unlock();
+}
+
+/* set the current priority level */
+uint8_t ucg_callout_mgr_set_prio(struct ucg_callout_mgr *cm, uint8_t new_prio)
+{
+       uint8_t old_prio;
+
+       old_prio = cm->cur_priority;
+       if (new_prio <= old_prio)
+               return old_prio;
+
+       cm->cur_priority = new_prio;
+       return old_prio;
+}
+
+/* restore the current priority level */
+void ucg_callout_mgr_restore_prio(struct ucg_callout_mgr *cm, uint8_t old_prio)
+{
+       cm->cur_priority = old_prio;
+}
+
+/* dump statistics about timers */
+void ucg_callout_dump_stats(struct ucg_callout_mgr *cm)
+{
+#ifdef UCG_CALLOUT_STATS
+       printf("Timer statistics:\r\n");
+       printf("  schedule = %"PRIu32"\r\n", cm->stats.schedule);
+       printf("  stop = %"PRIu32"\r\n", cm->stats.stop);
+       printf("  manage = %"PRIu32"\r\n", cm->stats.manage);
+       printf("  max_recursion = %"PRIu32"\r\n", cm->stats.max_recursion);
+       printf("  delayed = %"PRIu32"\r\n", cm->stats.delayed);
+       printf("  hard_delayed = %"PRIu32"\r\n", cm->stats.hard_delayed);
+
+       printf("  cur_scheduled = %u\r\n", cm->stats.cur_scheduled);
+       printf("  cur_expired = %u\r\n", cm->stats.cur_expired);
+       printf("  cur_running = %u\r\n", cm->stats.cur_running);
+#else
+       printf("No timer statistics, UCG_CALLOUT_STATS is disabled\r\n");
+#endif
+}
diff --git a/lib/cirbuf/include/ucg_cirbuf.h b/lib/cirbuf/include/ucg_cirbuf.h
new file mode 100644 (file)
index 0000000..c6bed51
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_CIRBUF_H_
+#define UCG_CIRBUF_H_
+
+#include <stdio.h>
+
+/**
+ * A circular buffer.
+ */
+struct ucg_cirbuf {
+       unsigned maxlen; /**< Total length of the fifo (number of elements). */
+       unsigned start;  /**< Index of the first element. */
+       unsigned len;    /**< Current len of fifo. */
+       char *buf;       /**< Pointer to the data buffer. */
+};
+
+/**
+ * Initialize a circular buffer.
+ *
+ * @param cbuf
+ *   A pointer to an uninitialized circular buffer structure.
+ * @param buf
+ *   The buffer used to store the data.
+ * @param start
+ *   The index of head at initialization.
+ * @param maxlen
+ *   The size of the buffer.
+ */
+void ucg_cirbuf_init(struct ucg_cirbuf *cbuf, char *buf, unsigned start,
+       unsigned maxlen);
+
+/**
+ * Check if the circular buffer is full.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @return
+ *   1 if the circular buffer is full, else 0.
+ */
+static inline int ucg_cirbuf_is_full(const struct ucg_cirbuf *cbuf)
+{
+       return cbuf->len == cbuf->maxlen;
+}
+
+/**
+ * Check if the circular buffer is empty.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @return
+ *   1 if the circular buffer is empty, else 0.
+ */
+static inline int ucg_cirbuf_is_empty(const struct ucg_cirbuf *cbuf)
+{
+       return cbuf->len == 0;
+}
+
+/**
+ * Get the length of data in the circular buffer.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @return
+ *   The current length of data in the circular buffer.
+ */
+static inline unsigned ucg_cirbuf_get_len(const struct ucg_cirbuf *cbuf)
+{
+       return cbuf->len;
+}
+
+/**
+ * Get the size of the circular buffer.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @return
+ *   Return the maximum size of the circular buffer (used + free elements)
+ */
+static inline unsigned ucg_cirbuf_get_maxlen(const struct ucg_cirbuf *cbuf)
+{
+       return cbuf->maxlen;
+}
+
+/**
+ * Get the lenght of free space in the circular buffer.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @return
+ *   Return the length of free space.
+ */
+static inline unsigned ucg_cirbuf_get_freelen(const struct ucg_cirbuf *cbuf)
+{
+       return cbuf->maxlen - cbuf->len;
+}
+
+/**
+ * Iterator for a circular buffer
+ *
+ *   cirbuf: struct cirbuf pointer
+ *   i: an integer internally used in the macro
+ *   elt: char that takes the value for each iteration
+ */
+#define UCG_CIRBUF_FOREACH(cirbuf, i, elt)                             \
+       for (i = 0, elt = (cirbuf)->buf[(cirbuf)->start];               \
+            i < ((cirbuf)->len);                                       \
+            i ++,  elt = (cirbuf)->buf[((cirbuf)->start + i) %         \
+                    ((cirbuf)->maxlen)])
+
+/**
+ * Add a character at the head of the circular buffer.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @param c
+ *   The character to add.
+ * @return
+ *   Return 0 on success, or a negative value on error.
+ */
+int ucg_cirbuf_add_head_safe(struct ucg_cirbuf *cbuf, char c);
+
+/**
+ * Add a character at the head of the circular buffer.
+ *
+ * The function does not check that there is enough free space
+ * in the buffer, so it has to be done by the caller. If it's
+ * not the case, undefined behavior will occur.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @param c
+ *   The character to add.
+ */
+void ucg_cirbuf_add_head(struct ucg_cirbuf *cbuf, char c);
+
+/**
+ * Add a character at the tail of the circular buffer.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @param c
+ *   The character to add.
+ * @return
+ *   Return 0 on success, or a negative value on error.
+ */
+int ucg_cirbuf_add_tail_safe(struct ucg_cirbuf *cbuf, char c);
+
+/**
+ * Add a character at the tail of the circular buffer.
+ *
+ * The function does not check that there is enough free space
+ * in the buffer, so it has to be done by the caller. If it's
+ * not the case, undefined behavior will occur.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @param c
+ *   The character to add.
+ */
+void ucg_cirbuf_add_tail(struct ucg_cirbuf *cbuf, char c);
+
+/**
+ * Remove a char at the head of the circular buffer.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @return
+ *   Return 0 on success, or a negative value on error.
+ */
+int ucg_cirbuf_del_head_safe(struct ucg_cirbuf *cbuf);
+
+/**
+ * Remove a char at the head of the circular buffer.
+ *
+ * The function does not check that there is enough elements
+ * in the buffer, so it has to be done by the caller. If it's
+ * not the case, undefined behavior will occur.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ */
+void ucg_cirbuf_del_head(struct ucg_cirbuf *cbuf);
+
+/**
+ * Remove a char at the tail of the circular buffer.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @return
+ *   Return 0 on success, or a negative value on error.
+ */
+int ucg_cirbuf_del_tail_safe(struct ucg_cirbuf *cbuf);
+
+/**
+ * Remove a char at the tail of the circular buffer.
+ *
+ * The function does not check that there is enough elements
+ * in the buffer, so it has to be done by the caller. If it's
+ * not the case, undefined behavior will occur.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ */
+void ucg_cirbuf_del_tail(struct ucg_cirbuf *cbuf);
+
+/**
+ * Return the element at the tail of the circular buffer.
+ *
+ * The circular buffer must not be empty or an undefined character
+ * will be returned.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @return
+ *   The character at the tail of the circular buffer.
+ */
+char ucg_cirbuf_get_head(const struct ucg_cirbuf *cbuf);
+
+/**
+ * Return the element at the tail of the circular buffer.
+ *
+ * The circular buffer must not be empty or an undefined character
+ * will be returned.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @return
+ *   The character at the tail of the circular buffer.
+ */
+char ucg_cirbuf_get_tail(const struct ucg_cirbuf *cbuf);
+
+/**
+ * Add a buffer at the head of the circular buffer.
+ *
+ * Add 'n' bytes of buffer pointed by 'buf' ad the head of th
+ * circular buffer.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @param buf
+ *   The pointer to the buffer.
+ * @param n
+ *   Number of bytes to add.
+ * @return
+ *   Return 0 on success, or a negative value on error.
+ */
+int ucg_cirbuf_add_buf_head(struct ucg_cirbuf *cbuf, const char *buf,
+       unsigned n);
+
+/**
+ * Add a buffer at the tail of the circular buffer.
+ *
+ * Add 'n' bytes of buffer pointed by 'buf' ad the tail of th
+ * circular buffer.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @param buf
+ *   The pointer to the buffer.
+ * @param n
+ *   Number of bytes to add.
+ * @return
+ *   Return 0 on success, or a negative value on error.
+ */
+int ucg_cirbuf_add_buf_tail(struct ucg_cirbuf *cbuf, const char *buf,
+       unsigned n);
+
+/**
+ * Remove chars at the head of the circular buffer.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @param n
+ *   Number of bytes to remove.
+ * @return
+ *   Return 0 on success, or a negative value on error.
+ */
+int ucg_cirbuf_del_buf_head(struct ucg_cirbuf *cbuf, unsigned n);
+
+/**
+ * Remove chars at the tail of the circular buffer.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @param n
+ *   Number of bytes to remove.
+ * @return
+ *   Return 0 on success, or a negative value on error.
+ */
+int ucg_cirbuf_del_buf_tail(struct ucg_cirbuf *cbuf, unsigned n);
+
+/**
+ * Copy multiple bytes from the head of the circular buffer.
+ *
+ * Copy a maximum of 'n' characters from the head of the circular buffer
+ * into a flat buffer pointed by 'buf'. If the circular buffer is
+ * smaller than n, less bytes are copied.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @param buf
+ *   The pointer to the buffer.
+ * @param n
+ *   Maximum number of bytes to copy.
+ * @return
+ *   Return the number of copied chars.
+ */
+int ucg_cirbuf_get_buf_head(const struct ucg_cirbuf *cbuf, char *buf,
+       unsigned n);
+
+/**
+ * Copy multiple bytes from the tail of the circular buffer.
+ *
+ * Copy a maximum of 'n' characters from the tail of the circular buffer
+ * into a flat buffer pointed by 'buf'. If the circular buffer is
+ * smaller than n, less bytes are copied.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ * @param buf
+ *   The pointer to the buffer.
+ * @param n
+ *   Maximum number of bytes to copy.
+ * @return
+ *   Return the number of copied chars.
+ */
+int ucg_cirbuf_get_buf_tail(const struct ucg_cirbuf *cbuf, char *buf,
+       unsigned n);
+
+/**
+ * Set the start of the data to the index 0 of the internal buffer.
+ *
+ * After a call to this function, it is possible for the caller to
+ * use cbuf->buf as a linear (read-only) buffer.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ */
+void ucg_cirbuf_align_left(struct ucg_cirbuf *cbuf);
+
+/**
+ * Set the end of the data to the last index of the internal buffer.
+ *
+ * After a call to this function, it is possible for the caller to
+ * use cbuf->buf as a linear (read-only) buffer.
+ *
+ * @param cbuf
+ *   The circular buffer pointer.
+ */
+void ucg_cirbuf_align_right(struct ucg_cirbuf *cbuf);
+
+#endif /* CIRBUF_H_ */
diff --git a/lib/cirbuf/ucg_cirbuf.c b/lib/cirbuf/ucg_cirbuf.c
new file mode 100644 (file)
index 0000000..05efb82
--- /dev/null
@@ -0,0 +1,624 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <errno.h>
+
+#include <ucg_cirbuf.h>
+
+/* init a circular buffer */
+void
+ucg_cirbuf_init(struct ucg_cirbuf *cbuf, char *buf, unsigned start,
+       unsigned maxlen)
+{
+       cbuf->maxlen = maxlen;
+       cbuf->len = 0;
+       cbuf->start = start;
+       cbuf->buf = buf;
+}
+
+/* return the index of the tail (contains an empty element) */
+static unsigned
+ucg_cirbuf_get_end(const struct ucg_cirbuf *cbuf)
+{
+       unsigned end;
+
+       end = cbuf->start + cbuf->len;
+       if (end >= cbuf->maxlen)
+               end -= cbuf->maxlen;
+
+       return end;
+}
+
+/* multiple add at head */
+int
+ucg_cirbuf_add_buf_head(struct ucg_cirbuf *cbuf, const char *buf, unsigned n)
+{
+       unsigned remain = n;
+       int copy_start;
+       unsigned copy_len;
+
+       if (remain == 0 || remain > ucg_cirbuf_get_freelen(cbuf))
+               return -EINVAL;
+
+       copy_start = cbuf->start - remain;
+       if (copy_start < 0)
+               copy_start += cbuf->maxlen;
+       cbuf->start = copy_start;
+       cbuf->len += remain;
+       copy_len = remain;
+       if ((unsigned)copy_start + copy_len >= cbuf->maxlen)
+               copy_len = cbuf->maxlen - copy_start;
+       if (copy_len > 0) {
+               memcpy(cbuf->buf + copy_start, buf, copy_len);
+               buf += copy_len;
+               remain -= copy_len;
+       }
+       if (remain == 0)
+               return n;
+
+       copy_len = remain;
+       memcpy(cbuf->buf, buf, copy_len);
+
+       return n;
+}
+
+/* multiple add at tail */
+int
+ucg_cirbuf_add_buf_tail(struct ucg_cirbuf *cbuf, const char *buf, unsigned n)
+{
+       unsigned remain = n;
+       unsigned copy_start, copy_len;
+
+       if (remain == 0 || remain > ucg_cirbuf_get_freelen(cbuf))
+               return -EINVAL;
+
+       copy_start = ucg_cirbuf_get_end(cbuf);
+       cbuf->len += remain;
+       copy_len = remain;
+       if (copy_start + copy_len >= cbuf->maxlen)
+               copy_len = cbuf->maxlen - copy_start;
+       if (copy_len > 0) {
+               memcpy(cbuf->buf + copy_start, buf, copy_len);
+               buf += copy_len;
+               remain -= copy_len;
+       }
+       if (remain == 0)
+               return n;
+
+       copy_len = remain;
+       memcpy(cbuf->buf, buf, copy_len);
+
+       return n;
+}
+
+/* single add at head */
+static inline void
+__cirbuf_add_head(struct ucg_cirbuf *cbuf, char c)
+{
+       unsigned start = cbuf->start;
+
+       if (start == 0)
+               start = cbuf->maxlen - 1;
+       else
+               start = start - 1;
+       cbuf->buf[start] = c;
+       cbuf->start = start;
+       cbuf->len++;
+}
+
+/* single add at head, checking if full first */
+int
+ucg_cirbuf_add_head_safe(struct ucg_cirbuf *cbuf, char c)
+{
+       if (!ucg_cirbuf_is_full(cbuf)) {
+               __cirbuf_add_head(cbuf, c);
+               return 0;
+       }
+       return -EINVAL;
+}
+
+/* single add at head */
+void
+ucg_cirbuf_add_head(struct ucg_cirbuf *cbuf, char c)
+{
+       __cirbuf_add_head(cbuf, c);
+}
+
+/* single add at tail */
+static inline void
+__cirbuf_add_tail(struct ucg_cirbuf *cbuf, char c)
+{
+       unsigned end = ucg_cirbuf_get_end(cbuf);
+
+       cbuf->buf[end] = c;
+       cbuf->len++;
+}
+
+/* single add at tail, checking if full first */
+int
+ucg_cirbuf_add_tail_safe(struct ucg_cirbuf *cbuf, char c)
+{
+       if (!ucg_cirbuf_is_full(cbuf)) {
+               __cirbuf_add_tail(cbuf, c);
+               return 0;
+       }
+       return -EINVAL;
+}
+
+/* single add at tail */
+void
+ucg_cirbuf_add_tail(struct ucg_cirbuf *cbuf, char c)
+{
+       __cirbuf_add_tail(cbuf, c);
+}
+
+/* multiple delete at head */
+int
+ucg_cirbuf_del_buf_head(struct ucg_cirbuf *cbuf, unsigned size)
+{
+       if (size == 0 || size > ucg_cirbuf_get_len(cbuf))
+               return -EINVAL;
+
+       cbuf->len -= size;
+       if (ucg_cirbuf_is_empty(cbuf)) {
+               cbuf->start += size - 1;
+               cbuf->start %= cbuf->maxlen;
+       }
+       else {
+               cbuf->start += size;
+               cbuf->start %= cbuf->maxlen;
+       }
+       return 0;
+}
+
+/* multiple delete at tail */
+int
+ucg_cirbuf_del_buf_tail(struct ucg_cirbuf *cbuf, unsigned size)
+{
+       if (size == 0 || size > ucg_cirbuf_get_len(cbuf))
+               return -EINVAL;
+
+       cbuf->len -= size;
+       return 0;
+}
+
+/* single del at head */
+static inline void
+__cirbuf_del_head(struct ucg_cirbuf *cbuf)
+{
+       cbuf->len --;
+       if (!ucg_cirbuf_is_empty(cbuf)) {
+               cbuf->start ++;
+               cbuf->start %= cbuf->maxlen;
+       }
+}
+
+/* single del at head, checking if empty first */
+int
+ucg_cirbuf_del_head_safe(struct ucg_cirbuf *cbuf)
+{
+       if (cbuf && !ucg_cirbuf_is_empty(cbuf)) {
+               __cirbuf_del_head(cbuf);
+               return 0;
+       }
+       return -EINVAL;
+}
+
+/* single del at head */
+void
+ucg_cirbuf_del_head(struct ucg_cirbuf *cbuf)
+{
+       __cirbuf_del_head(cbuf);
+}
+
+/* single del at tail */
+static inline void
+__cirbuf_del_tail(struct ucg_cirbuf *cbuf)
+{
+       cbuf->len--;
+}
+
+/* single del at tail, checking if empty first */
+int
+ucg_cirbuf_del_tail_safe(struct ucg_cirbuf *cbuf)
+{
+       if (cbuf && !ucg_cirbuf_is_empty(cbuf)) {
+               __cirbuf_del_tail(cbuf);
+               return 0;
+       }
+       return -EINVAL;
+}
+
+/* single del at tail */
+void
+ucg_cirbuf_del_tail(struct ucg_cirbuf *cbuf)
+{
+       __cirbuf_del_tail(cbuf);
+}
+
+/* convert to buffer */
+int
+ucg_cirbuf_get_buf_head(const struct ucg_cirbuf *cbuf, char *buf, unsigned n)
+{
+       unsigned remain = n;
+       unsigned cirbuf_len, copy_start, copy_len;
+
+       cirbuf_len = ucg_cirbuf_get_len(cbuf);
+       if (remain >= cirbuf_len)
+               remain = cirbuf_len;
+
+       if (remain == 0)
+               return 0;
+
+       copy_start = cbuf->start;
+       copy_len = remain;
+       if (copy_start + copy_len >= cbuf->maxlen)
+               copy_len = cbuf->maxlen - copy_start;
+       if (copy_len > 0) {
+               memcpy(buf, cbuf->buf + copy_start, copy_len);
+               buf += copy_len;
+               remain -= copy_len;
+       }
+       if (remain == 0)
+               return n;
+
+       copy_len = remain;
+       memcpy(buf, cbuf->buf, copy_len);
+
+       return n;
+}
+
+/* convert to buffer */
+int
+ucg_cirbuf_get_buf_tail(const struct ucg_cirbuf *cbuf, char *buf, unsigned n)
+{
+       unsigned remain = n;
+       int copy_start;
+       unsigned cirbuf_len, copy_len;
+
+       cirbuf_len = ucg_cirbuf_get_len(cbuf);
+       if (remain >= cirbuf_len)
+               remain = cirbuf_len;
+
+       if (remain == 0)
+               return 0;
+
+       copy_start = ucg_cirbuf_get_end(cbuf) - remain;
+       if (copy_start < 0)
+               copy_start += cbuf->maxlen;
+       copy_len = remain;
+       if ((unsigned)copy_start + copy_len >= cbuf->maxlen)
+               copy_len = cbuf->maxlen - copy_start;
+       if (copy_len > 0) {
+               memcpy(buf, cbuf->buf + copy_start, copy_len);
+               buf += copy_len;
+               remain -= copy_len;
+       }
+       if (remain == 0)
+               return n;
+
+       copy_len = remain;
+       memcpy(buf, cbuf->buf, copy_len);
+
+       return n;
+}
+
+/* get head */
+char
+ucg_cirbuf_get_head(const struct ucg_cirbuf *cbuf)
+{
+       return cbuf->buf[cbuf->start];
+}
+
+/* get tail */
+char
+ucg_cirbuf_get_tail(const struct ucg_cirbuf *cbuf)
+{
+       unsigned end;
+
+       /* should not happen */
+       if (cbuf->len == 0)
+               return 0;
+
+       end = cbuf->start + cbuf->len - 1;
+       if (end >= cbuf->maxlen)
+               end -= cbuf->maxlen;
+       return cbuf->buf[end];
+}
+
+static void
+__ucg_cirbuf_shift(struct ucg_cirbuf *cbuf, unsigned n)
+{
+       char tmp, tmp2;
+       unsigned start, cur, min;
+
+       start = 0;
+       cur = 0;
+       tmp = cbuf->buf[0];
+       min = cbuf->maxlen;
+
+       while (1) {
+               cur = cur + n;
+               if (cur >= cbuf->maxlen)
+                       cur -= cbuf->maxlen;
+               tmp2 = cbuf->buf[cur];
+               cbuf->buf[cur] = tmp;
+               tmp = tmp2;
+               if (cur == start) {
+                       if ((cur + 1) == min)
+                               break;
+                       cur++;
+                       tmp = cbuf->buf[cur];
+                       start = cur;
+               } else if (cur < min) {
+                       min = cur;
+               }
+       }
+
+       cbuf->start += n;
+       if (cbuf->start >= cbuf->maxlen)
+               cbuf->start -= cbuf->maxlen;
+}
+
+void ucg_cirbuf_align_left(struct ucg_cirbuf *cbuf)
+{
+       __ucg_cirbuf_shift(cbuf, cbuf->maxlen - cbuf->start);
+}
+
+void ucg_cirbuf_align_right(struct ucg_cirbuf *cbuf)
+{
+       unsigned end = ucg_cirbuf_get_end(cbuf);
+       __ucg_cirbuf_shift(cbuf, cbuf->maxlen - end);
+}
+
+#ifdef TEST_CIRBUF
+#include <stdint.h>
+#include <assert.h>
+
+void dump_it(struct ucg_cirbuf * cbuf)
+{
+       unsigned i;
+       int idx;
+       char e;
+
+       printf("sta=%2.2d len=%2.2d/%2.2d { ",
+               cbuf->start,
+               ucg_cirbuf_get_len(cbuf),
+               ucg_cirbuf_get_maxlen(cbuf));
+
+       for (i = 0; i < ucg_cirbuf_get_maxlen(cbuf); i++) {
+               idx = i - cbuf->start;
+               if (idx < 0)
+                       idx += cbuf->maxlen;
+               if (idx < (int)ucg_cirbuf_get_len(cbuf))
+                       printf("%2.2x, ", cbuf->buf[i] & 0xFF);
+               else
+                       printf("XX, ");
+       }
+       printf("}  -> ");
+
+       printf("[ ");
+       UCG_CIRBUF_FOREACH(cbuf, i, e) {
+               printf("%2.2x, ", e & 0xFF);
+       }
+       printf("]\n");
+}
+
+int main(void)
+{
+       unsigned i;
+       struct ucg_cirbuf my_fifo;
+       char fifo_buf[16];
+
+       char buf1[] = { 0x10, 0x11, 0x12 };
+       char buf2[] = { 0x20, 0x21, 0x22, 0x23 };
+
+       char tmp_buf[16];
+       char ref_buf[] = { 0x20, 0x21, 0x22, 0x23, 0x01, 0x10, 0x11, 0x12 };
+
+       /* Test 1 */
+
+       printf("Test 1\n");
+
+       ucg_cirbuf_init(&my_fifo, fifo_buf, 0, 4);
+       assert(ucg_cirbuf_is_empty(&my_fifo));
+       assert(!ucg_cirbuf_is_full(&my_fifo));
+       dump_it(&my_fifo);
+
+       ucg_cirbuf_add_tail(&my_fifo, 1);
+       assert(ucg_cirbuf_get_head(&my_fifo) == 1);
+       assert(ucg_cirbuf_get_tail(&my_fifo) == 1);
+       dump_it(&my_fifo);
+
+
+       ucg_cirbuf_add_tail(&my_fifo, 2);
+       assert(!ucg_cirbuf_is_empty(&my_fifo));
+       assert(!ucg_cirbuf_is_full(&my_fifo));
+       dump_it(&my_fifo);
+
+       ucg_cirbuf_add_tail(&my_fifo, 3);
+       assert(ucg_cirbuf_get_head(&my_fifo) == 1);
+       assert(ucg_cirbuf_get_tail(&my_fifo) == 3);
+       dump_it(&my_fifo);
+
+       ucg_cirbuf_add_tail(&my_fifo, 4);
+       assert(!ucg_cirbuf_is_empty(&my_fifo));
+       assert(ucg_cirbuf_is_full(&my_fifo));
+       dump_it(&my_fifo);
+
+       ucg_cirbuf_del_tail(&my_fifo);
+       dump_it(&my_fifo);
+       assert(ucg_cirbuf_get_tail(&my_fifo) == 3);
+       assert(ucg_cirbuf_get_head(&my_fifo) == 1);
+
+       ucg_cirbuf_del_head(&my_fifo);
+       assert(ucg_cirbuf_get_tail(&my_fifo) == 3);
+       assert(ucg_cirbuf_get_head(&my_fifo) == 2);
+       dump_it(&my_fifo);
+
+       ucg_cirbuf_del_head(&my_fifo);
+       assert(ucg_cirbuf_get_tail(&my_fifo) == 3);
+       assert(ucg_cirbuf_get_head(&my_fifo) == 3);
+       dump_it(&my_fifo);
+
+       ucg_cirbuf_del_head(&my_fifo);
+       assert(ucg_cirbuf_is_empty(&my_fifo));
+       dump_it(&my_fifo);
+
+
+       /* Test 2 */
+
+       printf("Test 2\n");
+
+       ucg_cirbuf_init(&my_fifo, fifo_buf, 2, 4);
+       dump_it(&my_fifo);
+
+       ucg_cirbuf_add_head(&my_fifo, 4);
+       assert(ucg_cirbuf_get_head(&my_fifo) == 4);
+       assert(ucg_cirbuf_get_tail(&my_fifo) == 4);
+       dump_it(&my_fifo);
+
+
+       ucg_cirbuf_add_head(&my_fifo, 3);
+       assert(!ucg_cirbuf_is_empty(&my_fifo));
+       assert(!ucg_cirbuf_is_full(&my_fifo));
+       dump_it(&my_fifo);
+
+       ucg_cirbuf_add_head(&my_fifo, 2);
+       assert(ucg_cirbuf_get_head(&my_fifo) == 2);
+       assert(ucg_cirbuf_get_tail(&my_fifo) == 4);
+       dump_it(&my_fifo);
+
+       ucg_cirbuf_add_head(&my_fifo, 1);
+       assert(!ucg_cirbuf_is_empty(&my_fifo));
+       assert(ucg_cirbuf_is_full(&my_fifo));
+       dump_it(&my_fifo);
+
+
+       /* Test 3 */
+
+       printf("Test 3\n");
+
+       for (i = 0; i < 16; i++) {
+               ucg_cirbuf_init(&my_fifo, fifo_buf, i, 16);
+               dump_it(&my_fifo);
+               ucg_cirbuf_add_buf_head(&my_fifo, buf1, sizeof(buf1));
+               dump_it(&my_fifo);
+               ucg_cirbuf_add_head(&my_fifo, 1);
+               dump_it(&my_fifo);
+               ucg_cirbuf_add_buf_head(&my_fifo, buf2, sizeof(buf2));
+               dump_it(&my_fifo);
+               ucg_cirbuf_get_buf_head(&my_fifo, tmp_buf, sizeof(tmp_buf));
+               assert(memcmp(tmp_buf, ref_buf, sizeof(ref_buf)) == 0);
+       }
+
+       /* Test 4 */
+
+       printf("Test 4\n");
+
+       for (i = 0; i < 16; i++) {
+               ucg_cirbuf_init(&my_fifo, fifo_buf, i, 16);
+               dump_it(&my_fifo);
+               ucg_cirbuf_add_buf_tail(&my_fifo, buf2, sizeof(buf2));
+               dump_it(&my_fifo);
+               ucg_cirbuf_add_tail(&my_fifo, 1);
+               dump_it(&my_fifo);
+               ucg_cirbuf_add_buf_tail(&my_fifo, buf1, sizeof(buf1));
+               dump_it(&my_fifo);
+               ucg_cirbuf_get_buf_tail(&my_fifo, tmp_buf, sizeof(tmp_buf));
+               assert(memcmp(tmp_buf, ref_buf, sizeof(ref_buf)) == 0);
+
+               printf("align left\n");
+               ucg_cirbuf_align_left(&my_fifo);
+               dump_it(&my_fifo);
+               ucg_cirbuf_get_buf_tail(&my_fifo, tmp_buf, sizeof(tmp_buf));
+               assert(memcmp(tmp_buf, ref_buf, sizeof(ref_buf)) == 0);
+               assert(my_fifo.start == 0);
+
+               printf("align right\n");
+               ucg_cirbuf_align_right(&my_fifo);
+               dump_it(&my_fifo);
+               ucg_cirbuf_get_buf_tail(&my_fifo, tmp_buf, sizeof(tmp_buf));
+               assert(memcmp(tmp_buf, ref_buf, sizeof(ref_buf)) == 0);
+               assert(my_fifo.start + my_fifo.len == my_fifo.maxlen);
+       }
+
+       /* Test 5 */
+
+       printf("Test 5\n");
+
+       ucg_cirbuf_init(&my_fifo, fifo_buf, 10, 16);
+       dump_it(&my_fifo);
+       i = 0;
+       while (ucg_cirbuf_add_tail_safe(&my_fifo, i) == 0)
+               i++;
+       dump_it(&my_fifo);
+       ucg_cirbuf_del_buf_tail(&my_fifo, 10);
+       dump_it(&my_fifo);
+       assert(ucg_cirbuf_get_len(&my_fifo) == 6);
+       assert(ucg_cirbuf_del_buf_tail(&my_fifo, 10) != 0);
+       assert(ucg_cirbuf_get_tail(&my_fifo) == 5);
+       assert(ucg_cirbuf_get_head(&my_fifo) == 0);
+
+       return 0;
+}
+#endif
diff --git a/lib/cmd/include/ucg_cmd.h b/lib/cmd/include/ucg_cmd.h
new file mode 100644 (file)
index 0000000..c9e80ec
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_CMD_H_
+#define UCG_CMD_H_
+
+#include <stdarg.h>
+#include <stdio.h>
+#ifdef UCG_CMD_HAVE_TERMIOS
+#include <termios.h>
+#endif
+
+#include "ucg_cmd_parse.h"
+#include "ucg_cmd_rdline.h"
+
+/**
+ * A command line structure.
+ */
+struct ucg_cmd {
+       ucg_cmd_ctx_t *ctx;      /**< The list of commands for this context. */
+       struct ucg_rdline rdl;   /**< The associated rdline structure. */
+       char prompt[UCG_RDLINE_PROMPT_SIZE];  /**< The command line prompt. */
+#ifdef UCG_CMD_HAVE_TERMIOS
+       struct termios oldterm;  /**< The old termios info */
+#endif
+       void *opaque;            /**< A user opaque pointer. */
+};
+
+/**
+ * Allocate and initialize a new command line structure
+ *
+ * Allocate and initialize a new command line structure, using the
+ * specified context, prompt and input/output streams.
+ *
+ * Once unused, the command line structure should be freed using
+ * ucg_cmd_free().
+ *
+ * @param ctx
+ *   The command line context.
+ * @param prompt
+ *   The command line prompt. The string is copied in the ucg_cmd
+ *   structure. The prompt must be smaller than UCG_RDLINE_PROMPT_SIZE.
+ * @param f_in
+ *   The input stream.
+ * @param f_out
+ *   The output stream.
+ * @return
+ *   The newly allocated command line structure.
+ */
+struct ucg_cmd *ucg_cmd_new(ucg_cmd_ctx_t *ctx,
+       const char *prompt, FILE *f_in, FILE *f_out);
+
+/**
+ * Initialize a new command line structure
+ *
+ * Initialize a command line structure, using the specified prompt and
+ * specified input/output streams.
+ *
+ * @param cl
+ *   The uninitialize command line structure
+ * @param ctx
+ *   The command line context
+ * @param prompt
+ *   The command line prompt. The string is copied in the ucg_cmd
+ *   structure. The prompt must be smaller than UCG_RDLINE_PROMPT_SIZE.
+ * @param f_in
+ *   The input stream
+ * @param f_out
+ *   The output stream
+ */
+void ucg_cmd_init(struct ucg_cmd *cl, ucg_cmd_ctx_t *ctx,
+       const char *prompt, FILE *f_in, FILE *f_out);
+
+/**
+ * Set the prompt of the given command line.
+ */
+void ucg_cmd_set_prompt(struct ucg_cmd *cl, const char *prompt);
+
+/**
+ * Free a previously allocated command line
+ *
+ * The structure pointed by cl is freed. The streams f_in and f_out are
+ * closed, except if it's stdin, stdout, or stderr. Note: the user
+ * should call ucg_cmd_quit() before calling this function.
+ *
+ * @param cl
+ *   The command line pointer.
+ */
+void ucg_cmd_free(struct ucg_cmd *cl);
+
+/**
+ * Parse a file use the given cmd context
+ *
+ * The output file descriptor (used for instance by cmd_printf) is
+ * given as a parameter. It can be -1 if no output is required.
+ */
+struct ucg_cmd *ucg_cmd_file_new(ucg_cmd_ctx_t *ctx,
+       const char *prompt, const char *path, FILE *f_out);
+
+/**
+ * Print data on the output of the command line
+ *
+ * This function is a wrapper to rdline_printf().
+ *
+ * @param cl
+ *   The command line pointer.
+ * @param fmt
+ *   The format string, followed by variable arguments.
+ * @return
+ *   On success, return the number of characters printed (not including
+ *   the trailing '\0'). On error, a negative value is returned.
+ */
+int ucg_cmd_printf(struct ucg_cmd *cl, const char *fmt, ...);
+
+/**
+ * Push an input buffer to the command line.
+ *
+ * Typically, this function is called by ucg_cmd_interact() to send the
+ * input characters to the command line process. It can also be called
+ * by a user, for instance when new data is received from an input
+ * socket.
+ *
+ * @param cl
+ *   The command line pointer.
+ * @param buf
+ *   The address of the input buffer.
+ * @param size
+ *   The len of the input buffer.
+ * @return
+ *   The function returns the number of processed characters, or a
+ *   negative value on error (ex: EOF reached or command line exited).
+ */
+int ucg_cmd_in(struct ucg_cmd *cl, const char *buf, int size);
+
+/* flags for ucg_cmd_interact */
+#define UCG_CMD_F_IGNORE_EOF 0x0001   /**< ignore eof when polling */
+
+/**
+ * Start command line on configured file descriptor. This function
+ * loops until the user explicitelly call ucg_cmd_quit(), or if the
+ * input fd reaches EOF.
+ *
+ * @param cl
+ *   The command line pointer
+ * @param flags
+ *   Any flags from UCG_CMD_F_*
+ *   - UCG_CMD_F_IGNORE_EOF: on eof, clear error and continue polling
+ */
+void ucg_cmd_interact(struct ucg_cmd *cl, unsigned flags);
+
+/**
+ * Stop a running command line.
+ *
+ * Actually it will call ucg_rdline_quit() on the associated rdline.
+ *
+ * @param cl
+ *   The command line pointer.
+ */
+void ucg_cmd_quit(struct ucg_cmd *cl);
+
+#endif /* UCG_CMD_H_ */
diff --git a/lib/cmd/include/ucg_cmd_parse.h b/lib/cmd/include/ucg_cmd_parse.h
new file mode 100644 (file)
index 0000000..37d93c1
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_CMD_PARSE_H_
+#define UCG_CMD_PARSE_H_
+
+#include <sys/types.h>
+
+#ifndef offsetof
+#define offsetof(type, field)  ((size_t) &( ((type *)0)->field) )
+#endif
+
+// XXX config
+#define UCG_CMD_MAX_TOKEN_SIZE 32 /* including '\0' */
+#define UCG_CMD_MAX_DSTBUF_SIZE 128
+
+/**
+ * A token header.
+ *
+ * Stores a pointer to the ops struct, and the offset: the place to
+ * write the parsed result in the destination structure.
+ */
+struct ucg_cmd_tk_hdr {
+       struct ucg_cmd_tk_ops *ops;
+       unsigned int offset;
+};
+typedef struct ucg_cmd_tk_hdr ucg_cmd_tk_hdr_t;
+
+/**
+ * Operations on token.
+ */
+struct ucg_cmd_tk_ops {
+       /**
+        * parse() converts a buffer containing a token into its parsed
+        * value. Ex: an integer string is converted into its integer
+        * value. The result is stored in "result" whose size is
+        * "res_size". It returns 0 on success and a negative value on
+        * error.
+        */
+       int (*parse)(ucg_cmd_tk_hdr_t *tk, const char *line,
+               void *result, unsigned int res_size);
+
+       /**
+        * complete_start() prepares a completion operation. The
+        * "opaque" arg is an opaque pointer that will be given to
+        * complete_iterate() function. It can be used to store private
+        * data for this completion. For each complete_start() call, the
+        * user must call complete_end() at the end of iterations (if
+        * defined) in case some data have to be freed.
+        *
+        * Return a negative value if completion is not possible, or 0
+        * on success.
+        */
+       int (*complete_start)(ucg_cmd_tk_hdr_t *tk, const char *line,
+               void **opaque);
+
+       /**
+        * complete_iterate() copy in "dst" buffer the next possible
+        * completion for this token. Return 0 on success (final
+        * completion = completion until the end of the token), 1 if
+        * it's an intermediate completion (token not fully completed),
+        * or a negative value on error (or when there is no more
+        * completion). Refer to ucg_cmd_complete_string_iterate() for
+        * an example.
+        */
+       int (*complete_iterate)(ucg_cmd_tk_hdr_t *tk, void **opaque,
+               char *dst, unsigned int dst_size);
+
+       /**
+        * complete_end() is called when the iteration on this token is
+        * finished, this function should free all things allocated
+        * during complete_start().
+        */
+       void (*complete_end)(ucg_cmd_tk_hdr_t *tk, void **opaque);
+
+       /**
+        * help() fills the dstbuf with the help for the token. It returns
+        * -1 on error and 0 on success.
+        */
+       int (*help)(ucg_cmd_tk_hdr_t *tk, char *dst, unsigned int dst_size);
+};
+
+struct ucg_cmd;
+
+/**
+ * Store a command, defined by a list of tokens and a callback function.
+ */
+struct ucg_cmd_inst {
+       /** callback function when the commend is parsed */
+       void (*f)(void *parsed_result, struct ucg_cmd *cl, void *opaque);
+       void *data;     /**< opaque pointer given as is to the callback */
+       char *help_str; /**< help for this command */
+       ucg_cmd_tk_hdr_t *tokens[]; /**< list of tokens */
+};
+typedef struct ucg_cmd_inst ucg_cmd_inst_t;
+
+/**
+ * A context is a list of commands.
+ */
+struct ucg_cmd_ctx {
+       const char *name;                /**< The name of the context */
+       const ucg_cmd_inst_t *insts[];   /**< List of commands */
+};
+typedef struct ucg_cmd_ctx ucg_cmd_ctx_t;
+
+/* return status for parsing */
+#define UCG_CMD_PARSE_SUCCESS        0
+#define UCG_CMD_PARSE_EMPTY         -1
+#define UCG_CMD_PARSE_NOMATCH       -2
+#define UCG_CMD_PARSE_AMBIGUOUS     -3
+#define UCG_CMD_PARSE_UNTERMINATED_QUOTE -4
+
+/**
+ * Try to parse a buffer according to the specified context. The
+ * argument linebuf must end with "\n\0".
+ *
+ * The function returns:
+ * - UCG_CMD_PARSE_SUCCESS (0) on success
+ * - UCG_CMD_PARSE_EMPTY if there is nothing to parse
+ * - UCG_CMD_PARSE_NOMATCH if line does not match any command
+ * - UCG_CMD_PARSE_AMBIGUOUS if several commands match
+ * - UCG_CMD_PARSE_UNTERMINATED_QUOTE if a quote is used incorrectly
+ */
+int ucg_cmd_parse(struct ucg_cmd *cl, const char *linebuf, void *opaque);
+
+/* return status for completion */
+#define UCG_CMD_COMPLETE_APPEND    0
+#define UCG_CMD_COMPLETE_NONE     -1
+#define UCG_CMD_COMPLETE_MANY     -2
+
+/**
+ * ucg_cmd_complete() tries to complete the buffer given as a parameter.
+ *
+ * It returns:
+ *   - UCG_CMD_COMPLETE_APPEND (0) on success, when a completion is
+ *     done (one possible choice). In this case, the chars are
+ *     appended in dst buffer.
+ *   - UCG_CMD_COMPLETE_NONE: error, no possible completion
+ *   - UCG_CMD_COMPLETE_MANY: error, many possble completions, need to call
+ *     ucg_cmd_help() function to see all the possibilities.
+ */
+int ucg_cmd_complete(struct ucg_cmd *cl, const char *buf, char *dst,
+       unsigned int size);
+
+/**
+ * Display a contextual help.
+ *
+ * @param cl
+ *   The command line pointer
+ * @param line
+ *   The current line buffer.
+ */
+void ucg_cmd_help(struct ucg_cmd *cl, const char *line);
+
+/**
+ * Check if the character ends a token
+ *
+ * @param c
+ *   The character.
+ * @return
+ *   True if (c == '\0' || iscomment(c) || isblank(c) ||
+ *   isendofline(c))
+ */
+int ucg_cmd_isendoftoken(char c);
+
+/**
+ * Quote a string and escape original quotes
+ *
+ * @param dst
+ *   The destination buffer.
+ * @param dstlen
+ *   The length of the destination buffer.
+ * @param src
+ *   The source buffer.
+ * @return
+ *   Return 0 on success, a negative value on error.
+ */
+int ucg_cmd_quote_token(char *dst, unsigned dstlen, const char *src);
+
+/**
+ * Get one token
+ *
+ * The function removes quotes (if any) and stop copying when the end of
+ * token is reached. The destination buffer is '\0'-terminated.
+ *
+ * @param dst
+ *   The destination buffer.
+ * @param dstlen
+ *   The length of the destination buffer.
+ * @param src
+ *   The source buffer.
+ * @return
+ *   Return the number of "consumed" bytes from the source buffer, or
+ *   a negative value on error
+ */
+int ucg_cmd_get_token(char *dst, unsigned dstlen, const char *src);
+
+#endif /* UCG_CMD_PARSE_H_ */
diff --git a/lib/cmd/include/ucg_cmd_parse_etheraddr.h b/lib/cmd/include/ucg_cmd_parse_etheraddr.h
new file mode 100644 (file)
index 0000000..88c3329
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PARSE_ETHERADDR_H_
+#define _PARSE_ETHERADDR_H_
+
+#include "ucg_cmd_parse.h"
+
+struct ucg_cmd_tk_etheraddr_data {
+       uint8_t flags;
+};
+
+struct ucg_cmd_tk_etheraddr {
+       struct ucg_cmd_tk_hdr hdr;
+};
+typedef struct ucg_cmd_tk_etheraddr ucg_cmd_tk_etheraddr_t;
+
+extern struct ucg_cmd_tk_ops ucg_cmd_tk_etheraddr_ops;
+
+#define UCG_CMD_TK_ETHERADDR(structure, field)     \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &cmd_token_etheraddr_ops,            \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+}
+
+
+#endif /* _PARSE_ETHERADDR_H_ */
diff --git a/lib/cmd/include/ucg_cmd_parse_file.h b/lib/cmd/include/ucg_cmd_parse_file.h
new file mode 100644 (file)
index 0000000..937c7a0
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_CMD_PARSE_FILE_H_
+#define UCG_CMD_PARSE_FILE_H_
+
+#include "ucg_cmd_parse.h"
+
+/* size of a parsed file */
+#define UCG_FILENAME_SIZE UCG_CMD_MAX_TOKEN_SIZE
+
+typedef char ucg_cmd_filename_t[UCG_FILENAME_SIZE];
+
+#define PARSE_FILE_F_CREATE    0x01 /* file does not necessarilly exist */
+#define PARSE_FILE_F_DIRECTORY 0x02 /* must be a directory */
+struct ucg_cmd_tk_file_data {
+       int flags;
+};
+
+struct ucg_cmd_tk_file {
+       struct ucg_cmd_tk_hdr hdr;
+       struct ucg_cmd_tk_file_data file_data;
+};
+typedef struct ucg_cmd_tk_file ucg_cmd_tk_file_t;
+
+extern struct ucg_cmd_tk_ops ucg_cmd_tk_file_ops;
+
+#define UCG_CMD_TK_FILE(structure, field, node_flags)  \
+{                                                              \
+       .hdr = {                                                \
+               .ops = &ucg_cmd_tk_file_ops,            \
+               .offset = offsetof(structure, field),           \
+       },                                                      \
+       .file_data = {                                          \
+               .flags = node_flags,                            \
+       },                                                      \
+}
+
+#endif /* UCG_CMD_PARSE_FILE_H_ */
diff --git a/lib/cmd/include/ucg_cmd_parse_ipaddr.h b/lib/cmd/include/ucg_cmd_parse_ipaddr.h
new file mode 100644 (file)
index 0000000..8eee078
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_CMD_PARSE_IPADDR_H_
+#define UCG_CMD_PARSE_IPADDR_H_
+
+#include "ucg_cmd_parse.h"
+
+#define UCG_CMD_IPADDR_V4      0x01
+#define UCG_CMD_IPADDR_V6      0x02
+#define UCG_CMD_IPADDR_NETWORK 0x04
+
+struct ucg_cmd_ipaddr {
+       uint8_t family;
+       union {
+               struct in_addr ipv4;
+               struct in6_addr ipv6;
+       } addr;
+       unsigned int prefixlen; /* in case of network only */
+};
+typedef struct ucg_cmd_ipaddr ucg_cmd_ipaddr_t;
+
+struct ucg_cmd_tk_ipaddr_data {
+       uint8_t flags;
+};
+
+struct ucg_cmd_tk_ipaddr {
+       struct ucg_cmd_tk_hdr hdr;
+       struct ucg_cmd_tk_ipaddr_data ipaddr_data;
+};
+typedef struct ucg_cmd_tk_ipaddr ucg_cmd_tk_ipaddr_t;
+
+extern struct ucg_cmd_tk_ops ucg_cmd_tk_ipaddr_ops;
+
+#define UCG_CMD_TK_IPADDR(structure, field)        \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &ucg_cmd_tk_ipaddr_ops,      \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+       .ipaddr_data = {                                    \
+               .flags = UCG_CMD_IPADDR_V4 |        \
+               UCG_CMD_IPADDR_V6,                          \
+       },                                                  \
+}
+
+#define UCG_CMD_TK_IPV4(structure, field)          \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &ucg_cmd_tk_ipaddr_ops,      \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+       .ipaddr_data = {                                    \
+               .flags = UCG_CMD_IPADDR_V4,                 \
+       },                                                  \
+}
+
+#define UCG_CMD_TK_IPV6(structure, field)          \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &ucg_cmd_tk_ipaddr_ops,      \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+       .ipaddr_data = {                                    \
+               .flags = UCG_CMD_IPADDR_V6,                 \
+       },                                                  \
+}
+
+#define UCG_CMD_TK_IPNET(structure, field)         \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &ucg_cmd_tk_ipaddr_ops,      \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+       .ipaddr_data = {                                    \
+               .flags = UCG_CMD_IPADDR_V4 |        \
+               UCG_CMD_IPADDR_V6 |                         \
+               UCG_CMD_IPADDR_NETWORK,             \
+       },                                                  \
+}
+
+#define UCG_CMD_TK_IPV4NET(structure, field)       \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &ucg_cmd_tk_ipaddr_ops,      \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+       .ipaddr_data = {                                    \
+               .flags = UCG_CMD_IPADDR_V4 |        \
+               UCG_CMD_IPADDR_NETWORK,             \
+       },                                                  \
+}
+
+#define UCG_CMD_TK_IPV6NET(structure, field)       \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &ucg_cmd_tk_ipaddr_ops,      \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+       .ipaddr_data = {                                    \
+               .flags = UCG_CMD_IPADDR_V4 |        \
+               UCG_CMD_IPADDR_NETWORK,             \
+       },                                                  \
+}
+
+#endif /* UCG_CMD_PARSE_IPADDR_H_ */
diff --git a/lib/cmd/include/ucg_cmd_parse_num.h b/lib/cmd/include/ucg_cmd_parse_num.h
new file mode 100644 (file)
index 0000000..d5292d1
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_CMD_PARSE_NUM_H_
+#define UCG_CMD_PARSE_NUM_H_
+
+#include "ucg_cmd_parse.h"
+
+enum ucg_cmd_numtype {
+       UINT8 = 0,
+       UINT16,
+       UINT32,
+       UINT64,
+       INT8,
+       INT16,
+       INT32,
+       INT64
+#ifndef NO_PARSE_FLOAT
+       ,FLOAT
+#endif
+};
+
+struct ucg_cmd_tk_num_data {
+       enum ucg_cmd_numtype type;
+};
+
+struct ucg_cmd_tk_num {
+       struct ucg_cmd_tk_hdr hdr;
+       struct ucg_cmd_tk_num_data num_data;
+};
+typedef struct ucg_cmd_tk_num ucg_cmd_tk_num_t;
+
+extern struct ucg_cmd_tk_ops ucg_cmd_tk_num_ops;
+
+#define UCG_CMD_TK_NUM(structure, field, numtype)         \
+{                                                         \
+       .hdr = {                                           \
+               .ops = &ucg_cmd_tk_num_ops,                \
+               .offset = offsetof(structure, field),      \
+       },                                                 \
+       .num_data = {                                      \
+               .type = numtype,                           \
+       },                                                 \
+}
+
+#endif /* UCG_CMD_PARSE_NUM_H_ */
diff --git a/lib/cmd/include/ucg_cmd_parse_string.h b/lib/cmd/include/ucg_cmd_parse_string.h
new file mode 100644 (file)
index 0000000..a127fab
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_CMD_PARSE_STRING_H_
+#define UCG_CMD_PARSE_STRING_H_
+
+#include "ucg_cmd_parse.h"
+
+/* size of a parsed string */
+#define UCG_STR_TOKEN_SIZE UCG_CMD_MAX_TOKEN_SIZE
+
+typedef char ucg_cmd_fixed_string_t[UCG_STR_TOKEN_SIZE];
+
+struct ucg_cmd_tk_string_data {
+       const char *str;
+};
+
+struct ucg_cmd_tk_string {
+       struct ucg_cmd_tk_hdr hdr;
+       struct ucg_cmd_tk_string_data string_data;
+};
+typedef struct ucg_cmd_tk_string ucg_cmd_tk_string_t;
+
+extern struct ucg_cmd_tk_ops ucg_cmd_tk_string_ops;
+
+#define UCG_CMD_TK_STRING(structure, field, string)  \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &ucg_cmd_tk_string_ops,      \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+       .string_data = {                                    \
+               .str = string,                              \
+       },                                                  \
+}
+
+#endif /* UCG_CMD_PARSE_STRING_H_ */
diff --git a/lib/cmd/include/ucg_cmd_rdline.h b/lib/cmd/include/ucg_cmd_rdline.h
new file mode 100644 (file)
index 0000000..564835b
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CMD_RDLINE_H_
+#define CMD_RDLINE_H_
+
+/**
+ * This file is a small equivalent to the GNU readline library, it was
+ * originally designed for very small systems (an 8-bits Atmel AVR
+ * microcontroller), but the library can now run on many emmbedded
+ * systems with or without OS.
+ *
+ * Obviously, it does not support as many things as the GNU readline,
+ * but at least it supports some interresting features like a kill
+ * buffer and a command history.
+ *
+ * It also have a feature that does not have the GNU readline (as far
+ * as I know): it is possible to have several instances of readline
+ * running at the same time, even on a monothread program, since it
+ * works with callbacks.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#include <ucg_cirbuf.h>
+#include <ucg_cmd_vt100.h>
+
+/* configuration */
+#define UCG_RDLINE_BUF_SIZE 32
+#define UCG_RDLINE_PROMPT_SIZE  16
+#define UCG_RDLINE_VT100_BUF_SIZE  8
+#define UCG_RDLINE_HISTORY_BUF_SIZE 64
+#define UCG_RDLINE_MAX_LINES 23  /* pager */
+
+enum ucg_rdline_status {
+       UCG_RDLINE_STOPPED,
+       UCG_RDLINE_RUNNING,
+       UCG_RDLINE_EXITED
+};
+
+struct ucg_rdline;
+
+/**
+ * type of callback given to ucg_rdline_help() to display the content of
+ * the help. The first argument is the rdline pointer. The other args
+ * are buffer and size.
+ */
+typedef int (ucg_rdline_printf_t)(struct ucg_rdline *rdl,
+       const char *fmt, ...);
+
+/**
+ * type of callback invoked when a command is parsed. "rdl" is a pointer
+ * to the ucg_rdline structure, "line" is a pointer to the current line
+ * string ('\0' terminated).
+ */
+typedef void (ucg_rdline_validate_t)(struct ucg_rdline *rdl,
+       const char *line);
+
+/**
+ * type of callback invoked when a completion is requested. "rdl" is a
+ * pointer to the ucg_rdline structure, "line" is a pointer to the
+ * current line string ('\0' terminated). The characters to append
+ * are written to dstbuf.
+ * Return 0 on success: dstbuf contains the characters to append to
+ *   the current line ('\0' terminated)
+ * Else return a negative value if no completion is performed.
+ */
+typedef int (ucg_rdline_complete_t)(struct ucg_rdline *rdl, const char *line,
+       char *dstbuf, unsigned int dstsize);
+
+/**
+ * callback invoked when a the help is requested. "rdl" is a pointer to
+ * the ucg_rdline structure, "line" is a pointer to the current line string
+ * ('\0' terminated).
+ */
+typedef void (ucg_rdline_help_t)(struct ucg_rdline *rdl, const char *line);
+
+typedef void (ucg_rdline_pager_cb_t)(struct ucg_rdline *, void *);
+
+struct ucg_rdline {
+       enum ucg_rdline_status status;
+       FILE *f_in;
+       FILE *f_out;
+
+       /* rdline bufs */
+       struct ucg_cirbuf left;
+       struct ucg_cirbuf right;
+       char left_buf[UCG_RDLINE_BUF_SIZE+1]; /* reserve 1 char for the \0 */
+       char right_buf[UCG_RDLINE_BUF_SIZE];
+
+       char prompt[UCG_RDLINE_PROMPT_SIZE];
+
+#ifndef UCG_CMD_NO_RDLINE_KILL_BUF
+       char kill_buf[UCG_RDLINE_BUF_SIZE];
+       unsigned int kill_size;
+#endif
+
+#ifndef UCG_CMD_NO_RDLINE_HISTORY
+       /* history */
+       struct ucg_cirbuf history;
+       char history_buf[UCG_RDLINE_HISTORY_BUF_SIZE];
+       int history_cur_line;
+#endif
+
+       /* callbacks and func pointers */
+       ucg_rdline_validate_t *validate;
+       ucg_rdline_complete_t *complete;
+       ucg_rdline_help_t *help;
+
+       /* vt100 parser */
+       struct ucg_cmd_vt100 vt100;
+
+       /* opaque pointer */
+       void *opaque;
+
+#ifndef UCG_CMD_NO_PAGER
+       char *pager_buf; /* buffer used to store paged data */
+       int pager_len; /* total len of buffer */
+       int pager_off; /* offset of next data */
+       int pager_lines; /* number of lines displayed */
+       ucg_rdline_pager_cb_t *pager_cb; /* callback once paging is finished */
+       void *pager_arg; /* argument of callback */
+       int pager_ret; /* saved return value */
+#endif
+};
+
+/**
+ * Init fields for a struct ucg_rdline.
+ *
+ * @param rdl A pointer to an uninitialized struct ucg_rdline
+ * @param fd_in
+ *   Input file descriptor
+ * @param fd_out
+ *   Output file descriptor
+ * @param validate
+ *   A pointer to the function to execute when the user validates the
+ *   buffer.
+ * @param complete
+ *   A pointer to the function to execute when the user completes the
+ *   buffer.
+ * @param help
+ *   A pointer to the function to execute when the user ask for
+ *   contextual help.
+ */
+void ucg_rdline_init(struct ucg_rdline *rdl,
+                FILE *f_in, FILE *f_out,
+                ucg_rdline_validate_t *validate,
+                ucg_rdline_complete_t *complete,
+                ucg_rdline_help_t *help);
+
+
+/**
+ * Init the current buffer, and display a prompt.
+ *
+ * Also set the rdline status to "running", overriding previous
+ * ucg_rdline_stop() or ucg_rdline_quit().
+ *
+ * @param rdl
+ *   A pointer to an initialized struct ucg_rdline
+ * @param prompt
+ *   A string containing the prompt, or NULL to keep existing one
+ */
+void ucg_rdline_newline(struct ucg_rdline *rdl, const char *prompt);
+
+/**
+ * Ignore all subsequent received chars.
+ *
+ * @param rdl
+ *   A pointer to a struct ucg_rdline
+ */
+void ucg_rdline_stop(struct ucg_rdline *rdl);
+
+/**
+ * Exit from running rdline loop
+ *
+ * Same than ucg_rdline_stop() except that next calls to ucg_rdline_char_in()
+ * will return UCG_RDLINE_RES_EXITED. Hence, any running rdline() function is
+ * interrupted.
+ *
+ * @param rdl
+ *   A pointer to a struct ucg_rdline
+ */
+void ucg_rdline_quit(struct ucg_rdline *rdl);
+
+/**
+ * Restart after a call to ucg_rdline_stop() or ucg_rdline_quit()
+ *
+ * @param rdl
+ *   A pointer to a struct ucg_rdline
+ */
+void ucg_rdline_restart(struct ucg_rdline *rdl);
+
+/**
+ * Redisplay the current buffer
+ *
+ * @param rdl
+ *   A pointer to a struct ucg_rdline
+ */
+void ucg_rdline_redisplay(struct ucg_rdline *rdl);
+
+/* return status for ucg_rdline_char_in() */
+#define UCG_RDLINE_RES_SUCCESS          0
+#define UCG_RDLINE_RES_VALIDATED        1
+#define UCG_RDLINE_RES_COMPLETED        2
+#define UCG_RDLINE_RES_NOT_RUNNING     -1
+#define UCG_RDLINE_RES_EOF             -2
+#define UCG_RDLINE_RES_EXITED          -3
+#define UCG_RDLINE_RES_CANNOT_COMPLETE -3
+
+/**
+ * Append a char to the readline buffer.
+ *
+ * @param rdl
+ *   A pointer to a struct ucg_rdline
+ * @param c
+ *   The character to append
+ * @return
+ *   - UCG_RDLINE_RES_VALIDATED when the line has been validated.
+ *   - UCG_RDLINE_RES_NOT_RUNNING if it is not running.
+ *   - UCG_RDLINE_RES_EOF if EOF (ctrl-d on an empty line).
+ *   - UCG_RDLINE_RES_EXITED if user called ucg_rdline_quit()
+ *   - Else return UCG_RDLINE_RES_SUCCESS.
+ */
+int ucg_rdline_char_in(struct ucg_rdline *rdl, char c);
+
+#define UCG_RDLINE_F_IGNORE_EOF 0x0001   /**< ignore eof when polling */
+
+/**
+ * Read (and edit) a line
+ *
+ * @param rdl
+ *   A pointer to a struct ucg_rdline
+ * @param prompt
+ *   The prompt string
+ * @param flags
+ *   Any flags from UCG_RDLINE_F_*
+ *   - UCG_RDLINE_F_IGNORE_EOF: on eof, clear error and continue polling
+ * @return
+ *   - UCG_RDLINE_RES_VALIDATED when the line has been validated.
+ *   - UCG_RDLINE_RES_NOT_RUNNING if it is not running.
+ *   - UCG_RDLINE_RES_EOF if EOF (ctrl-d on an empty line).
+ *   - UCG_RDLINE_RES_EXITED if user called ucg_rdline_quit()
+ */
+int ucg_rdline(struct ucg_rdline *rdl, const char *prompt, unsigned flags);
+
+/**
+ * write a buffer on rdline file descriptor
+ *
+ * @param rdl
+ *   The rdline descriptor
+ * @param buf
+ *   Pointer to the buffer
+ * @param count
+ *   Number of bytes to write
+ * @return
+ *   On success, the number of bytes written is returned (zero
+ *   indicates nothing was written). On error, -1 is returned, and
+ *   errno is set appropriately
+ */
+ssize_t ucg_rdline_write(struct ucg_rdline *rdl, void *buf, size_t count);
+
+/**
+ * write on rdline file descriptor according to a format string
+ *
+ * @param rdl
+ *   The rdline descriptor
+ * @param fmt
+ *   The format strings
+ * @return
+ *   On success, return the number of characters printed (not including
+ *   the trailing '\0'). On error, a negative value is returned.
+ */
+int ucg_rdline_printf(struct ucg_rdline *rdl, const char *fmt, ...);
+
+/**
+ * write on rdline file descriptor according to a format string
+ *
+ * @param rdl
+ *   The rdline descriptor
+ * @param fmt
+ *   The format strings
+ * @param ap
+ *   Variable argument list
+ * @return
+ *   Upon successful return, these functions return the number of
+ *   characters printed (not including the trailing '\0' used to end
+ *   output to strings). On error, a negative value is returned.
+ */
+int ucg_rdline_vprintf(struct ucg_rdline *rdl, const char *fmt, va_list ap);
+
+/**
+ * Return the current buffer, terminated by '\0'.
+ *
+ * @param rdl
+ *   A pointer to a struct ucg_rdline
+ * @return
+ *   The rdline buffer
+ */
+const char *ucg_rdline_get_buffer(struct ucg_rdline *rdl);
+
+/**
+ * Add the buffer to history.
+ *
+ * @param rdl
+ *   A pointer to a struct ucg_rdline
+ * @param buf
+ *   A buffer that is terminated by '\0'
+ * @return
+ *   - 0 on success
+ *   - negative on error
+ */
+int ucg_rdline_add_history(struct ucg_rdline *rdl, const char *buf);
+
+/**
+ * Clear current history
+ *
+ * @param rdl
+ *   A pointer to a struct ucg_rdline
+ */
+void ucg_rdline_clear_history(struct ucg_rdline *rdl);
+
+/**
+ * Get the i-th history item
+ *
+ * @param rdl
+ *   A pointer to a struct ucg_rdline
+ * @param i
+ *   The index of the history item
+ * @return
+ *   The i-th string of history, or NULL on error.
+ */
+const char *ucg_rdline_get_history_item(struct ucg_rdline *rdl, unsigned int i);
+
+#ifndef UCG_CMD_NO_PAGER
+/**
+ * Write data asynchronously (using pager if needed)
+ *
+ * If there is enough place to print data on the current page, it is
+ * printed synchronously. Else, a temporary buffer is allocated and
+ * the data is stored in it. When the main rdline is called again, the
+ * pager is flushed before parsing any other commands.
+ *
+ * @param rdl
+ *   The rdline descriptor
+ * @param buf
+ *   Buffer to be sent
+ * @param len
+ *   Length of buffer to be sent
+ * @return
+ *   On success, the number of bytes written is returned (zero
+ *   indicates nothing was written). On error, -1 is returned, and
+ *   errno is set appropriately
+ */
+ssize_t ucg_rdline_pager_write(struct ucg_rdline *rdl, void *buf, size_t len);
+
+/**
+ * Print data asynchronously (using pager if needed)
+ *
+ * If there is enough place to print data on the current page, it is
+ * printed synchronously. Else, a temporary buffer is allocated and
+ * the data is stored in it. When the main rdline is called again, the
+ * pager is flushed before parsing any other commands.
+ *
+ * @param rdl
+ *   The rdline descriptor
+ * @param fmt
+ *   The format strings
+ * @return
+ *   Upon successful return, these functions return the number of
+ *   characters printed (not including the trailing '\0' used to end
+ *   output to strings). On error, a negative value is returned.
+ */
+int ucg_rdline_pager_printf(struct ucg_rdline *rdl, const char *fmt, ...);
+
+/**
+ * Set the callback for the pager
+ *
+ * If there is some data in the pager to be printed, set a callback
+ * function that will be called when all the data will be printed. If
+ * the pager is empty, don't do anything and return -1.
+ * @param rdl
+ *   The rdline descriptor
+ * @return
+ *   - 0 if there is some data in the pager buffer and the callback
+ *     is loaded
+ *   - -1 if there is no data in pager buffer (in this case the callback
+ *     is not called)
+ */
+int ucg_rdline_pager_set_cb(struct ucg_rdline *rdl,
+       ucg_rdline_pager_cb_t *cb, void *arg);
+#endif
+
+#endif /* UCG_CMD_RDLINE_H_ */
diff --git a/lib/cmd/include/ucg_cmd_socket.h b/lib/cmd/include/ucg_cmd_socket.h
new file mode 100644 (file)
index 0000000..e4fa918
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_CMD_SOCKET_H_
+#define UCG_CMD_SOCKET_H_
+
+#ifdef UCG_CMD_HAVE_SOCKET
+
+#include <arpa/inet.h>
+
+/**
+ * Helper to create a tcpv4 socket
+ */
+int ucg_cmd_tcpv4_listen(in_addr_t addr, uint16_t port);
+
+/**
+ * Helper to create a tcpv6 socket
+ */
+int ucg_cmd_tcpv6_listen(const struct in6_addr *addr6, uint16_t port);
+
+/**
+ * Helper to create a unix socket
+ */
+int ucg_cmd_unix_listen(const char *filename);
+
+/**
+ * Helper to call accept() and create a new cmd instance
+ */
+struct ucg_cmd *ucg_cmd_accept(ucg_cmd_ctx_t *ctx,
+       const char *prompt, int s);
+#endif
+
+#endif /* UCG_CMD_SOCKET_H_ */
diff --git a/lib/cmd/include/ucg_cmd_termios.h b/lib/cmd/include/ucg_cmd_termios.h
new file mode 100644 (file)
index 0000000..c882444
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_CMD_TERMIOS_H_
+#define UCG_CMD_TERMIOS_H_
+
+struct ucg_cmd;
+
+/**
+ * Set the pty in raw mode
+ *
+ * Save the previous configuration in the command line structure.
+ * It is restored with ucg_termios_restore(). The function does
+ * nothing if the platform does not support termios.
+ */
+int ucg_cmd_termios_raw(struct ucg_cmd *cl);
+
+/**
+ * Restore saved termios settings
+ */
+int ucg_cmd_termios_restore(struct ucg_cmd *cl);
+
+#endif /* UCG_CMD_H_ */
diff --git a/lib/cmd/include/ucg_cmd_vt100.h b/lib/cmd/include/ucg_cmd_vt100.h
new file mode 100644 (file)
index 0000000..a2a10b8
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_CMD_VT100_H_
+#define UCG_CMD_VT100_H_
+
+#define ucg_vt100_bell         "\007"
+#define ucg_vt100_bs           "\010"
+#define ucg_vt100_bs_clear     "\010 \010"
+#define ucg_vt100_tab          "\011"
+#define ucg_vt100_crnl         "\012\015"
+#define ucg_vt100_clear_right  "\033[0K"
+#define ucg_vt100_clear_left   "\033[1K"
+#define ucg_vt100_clear_down   "\033[0J"
+#define ucg_vt100_clear_up     "\033[1J"
+#define ucg_vt100_clear_line   "\033[2K"
+#define ucg_vt100_clear_screen "\033[2J"
+#define ucg_vt100_up_arr       "\033\133\101"
+#define ucg_vt100_down_arr     "\033\133\102"
+#define ucg_vt100_right_arr    "\033\133\103"
+#define ucg_vt100_left_arr     "\033\133\104"
+#define ucg_vt100_multi_right  "\033\133%uC"
+#define ucg_vt100_multi_left   "\033\133%uD"
+#define ucg_vt100_suppr        "\033\133\063\176"
+#define ucg_vt100_home         "\033M\033E"
+#define ucg_vt100_word_left    "\033\142"
+#define ucg_vt100_word_right   "\033\146"
+
+/* Result of parsing : it must be synchronized with
+ * ucg_cmd_vt100_commands[] in vt100.c */
+#define UCG_CMD_KEY_UP_ARR 0
+#define UCG_CMD_KEY_DOWN_ARR 1
+#define UCG_CMD_KEY_RIGHT_ARR 2
+#define UCG_CMD_KEY_LEFT_ARR 3
+#define UCG_CMD_KEY_BKSPACE 4
+#define UCG_CMD_KEY_RETURN 5
+#define UCG_CMD_KEY_CTRL_A 6
+#define UCG_CMD_KEY_CTRL_E 7
+#define UCG_CMD_KEY_CTRL_K 8
+#define UCG_CMD_KEY_CTRL_Y 9
+#define UCG_CMD_KEY_CTRL_C 10
+#define UCG_CMD_KEY_CTRL_F 11
+#define UCG_CMD_KEY_CTRL_B 12
+#define UCG_CMD_KEY_SUPPR 13
+#define UCG_CMD_KEY_TAB 14
+#define UCG_CMD_KEY_CTRL_D 15
+#define UCG_CMD_KEY_CTRL_L 16
+#define UCG_CMD_KEY_RETURN2 17
+#define UCG_CMD_KEY_META_BKSPACE 18
+#define UCG_CMD_KEY_WLEFT 19
+#define UCG_CMD_KEY_WRIGHT 20
+#define UCG_CMD_KEY_HELP 21
+#define UCG_CMD_KEY_CTRL_W 22
+#define UCG_CMD_KEY_CTRL_P 23
+#define UCG_CMD_KEY_CTRL_N 24
+#define UCG_CMD_KEY_META_D 25
+
+extern const char *ucg_cmd_vt100_commands[];
+
+enum ucg_cmd_vt100_parser_state {
+       UCG_CMD_VT100_INIT,
+       UCG_CMD_VT100_ESCAPE,
+       UCG_CMD_VT100_ESCAPE_CSI
+};
+
+#define UCG_CMD_VT100_BUF_SIZE 8
+struct ucg_cmd_vt100 {
+       uint8_t bufpos;
+       char buf[UCG_CMD_VT100_BUF_SIZE];
+       enum ucg_cmd_vt100_parser_state state;
+};
+
+/**
+ * Init
+ */
+void ucg_vt100_init(struct ucg_cmd_vt100 *vt);
+
+#define UCG_VT100_STD_CHAR     -1
+#define UCG_VT100_NOT_COMPLETE -2
+/**
+ * Input a new character.
+ * Return UCG_VT100_STD_CHAR if the character is not part of a control sequence
+ * Return UCG_VT100_NOT_COMPLETE if c is not the last char of a control sequence
+ * Else return the index in ucg_vt100_commands[]
+ */
+int ucg_vt100_parser(struct ucg_cmd_vt100 *vt, char c);
+
+#endif /* UCG_CMD_VT100_H_ */
diff --git a/lib/cmd/ucg_cmd.c b/lib/cmd/ucg_cmd.c
new file mode 100644 (file)
index 0000000..61b9a70
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "ucg_cmd_parse.h"
+#include "ucg_cmd_rdline.h"
+#include "ucg_cmd.h"
+
+static void
+default_valid_buffer(struct ucg_rdline *rdl, const char *line)
+{
+       struct ucg_cmd *cl = rdl->opaque;
+       int ret;
+
+       ret = ucg_cmd_parse(cl, line, cl);
+       if (ret == UCG_CMD_PARSE_AMBIGUOUS)
+               ucg_cmd_printf(cl, "Ambiguous command\n");
+       else if (ret == UCG_CMD_PARSE_NOMATCH)
+               ucg_cmd_printf(cl, "Bad arguments\n");
+       else if (ret == UCG_CMD_PARSE_UNTERMINATED_QUOTE)
+               ucg_cmd_printf(cl, "Unterminated quote\n");
+}
+
+static int
+default_complete_buffer(struct ucg_rdline *rdl, const char *line,
+       char *dstbuf, unsigned int dstsize)
+{
+       int ret;
+       struct ucg_cmd *cl = rdl->opaque;
+       ret = ucg_cmd_complete(cl, line, dstbuf, dstsize);
+       if (ret == UCG_CMD_COMPLETE_APPEND)
+               return 0;
+       return -1;
+}
+
+
+static void
+default_help(struct ucg_rdline *rdl, const char *line)
+{
+       struct ucg_cmd *cl = rdl->opaque;
+
+       ucg_cmd_help(cl, line);
+}
+
+/* ---- Some rdline wrappers ---- */
+
+void
+ucg_cmd_set_prompt(struct ucg_cmd *cl, const char *prompt)
+{
+       snprintf(cl->prompt, sizeof(cl->prompt), "%s", prompt);
+}
+
+void
+ucg_cmd_init(struct ucg_cmd *cl, ucg_cmd_ctx_t *ctx,
+       const char *prompt, FILE *f_in, FILE *f_out)
+{
+       /* init cmd structure */
+       memset(cl, 0, sizeof(struct ucg_cmd));
+       cl->ctx = ctx;
+
+       /* init embedded rdline */
+       ucg_rdline_init(&cl->rdl, f_in, f_out,
+               default_valid_buffer,
+               default_complete_buffer,
+               default_help);
+
+       cl->rdl.opaque = cl;
+       ucg_cmd_set_prompt(cl, prompt);
+       ucg_rdline_newline(&cl->rdl, cl->prompt);
+}
+
+struct ucg_cmd *
+ucg_cmd_new(ucg_cmd_ctx_t *ctx, const char *prompt,
+       FILE *f_in, FILE *f_out)
+{
+       struct ucg_cmd *cl;
+
+       cl = malloc(sizeof(struct ucg_cmd));
+       if (cl == NULL)
+               return NULL;
+
+       ucg_cmd_init(cl, ctx, prompt, f_in, f_out);
+       return cl;
+}
+
+struct ucg_cmd *
+ucg_cmd_file_new(ucg_cmd_ctx_t *ctx, const char *prompt,
+       const char *path, FILE *f_out)
+{
+#if UCG_CMD_HAVE_FILE
+       FILE *f_in;
+
+       f_in = fopen(path, "r");
+       if (f_in == NULL)
+               return NULL;
+       return (ucg_cmd_new(ctx, prompt, f_in, f_out));
+#else
+       (void)ctx;
+       (void)prompt;
+       (void)path;
+       (void)f_out;
+       return NULL;
+#endif
+}
+
+void
+ucg_cmd_free(struct ucg_cmd *cl)
+{
+       struct ucg_rdline *rdl = &cl->rdl;
+
+       if (rdl->f_in != stdin)
+               fclose(rdl->f_in);
+       if (rdl->f_out != rdl->f_in && rdl->f_out != stdout &&
+               rdl->f_out != stderr)
+               fclose(rdl->f_out);
+       free(cl);
+}
+
+int
+ucg_cmd_printf(struct ucg_cmd *cl, const char *fmt, ...)
+{
+       va_list ap;
+       int ret;
+
+       va_start(ap, fmt);
+       ret = ucg_rdline_vprintf(&cl->rdl, fmt, ap);
+       va_end(ap);
+
+       return ret;
+}
+
+/* Push an input buffer in the command line. Typically, this function
+ * is called by ucg_cmd_interact() to send the input characters to the
+ * cmd process. It can also be called by a user callback function,
+ * when a buffer is received from the input socket.
+ *
+ * The function returns the number of processed characters, or a
+ * negative value on error (EOF reached or command line exited. */
+int
+ucg_cmd_in(struct ucg_cmd *cl, const char *buf, int size)
+{
+       int ret = 0;
+       int i;
+
+       for (i = 0; i < size; i++) {
+               ret = ucg_rdline_char_in(&cl->rdl, buf[i]);
+
+               if (ret == UCG_RDLINE_RES_VALIDATED &&
+                       cl->rdl.status == UCG_RDLINE_STOPPED)
+                       break;
+
+               if (ret == UCG_RDLINE_RES_VALIDATED)
+                       ucg_rdline_newline(&cl->rdl, cl->prompt);
+               else if (ret == UCG_RDLINE_RES_EOF)
+                       return -1;
+               else if (ret == UCG_RDLINE_RES_EXITED)
+                       return -1;
+       }
+       return i;
+}
+
+/* Interrupt a running command line (exits from ucg_cmd_interact) */
+void
+ucg_cmd_quit(struct ucg_cmd *cl)
+{
+       ucg_rdline_quit(&cl->rdl);
+}
+
+/* loop until the user explicitelly call ucg_cmd_quit(), or if the input
+ * fd reaches EOF. */
+void
+ucg_cmd_interact(struct ucg_cmd *cl, unsigned flags)
+{
+       int ret;
+       char c;
+
+       c = -1;
+       while (1) {
+               ret = fread(&c, 1, 1, cl->rdl.f_in);
+
+               if (ret == 0) {
+                       if (flags & UCG_CMD_F_IGNORE_EOF) {
+                               clearerr(cl->rdl.f_in);
+                               continue;
+                       }
+                       break;
+               }
+
+               if (ucg_cmd_in(cl, &c, 1) < 0)
+                       break;
+       }
+}
diff --git a/lib/cmd/ucg_cmd_parse.c b/lib/cmd/ucg_cmd_parse.c
new file mode 100644 (file)
index 0000000..587a250
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include "ucg_cmd_parse.h"
+#include "ucg_cmd.h"
+
+//#define debug_printf printf
+#define debug_printf(args...) do {} while(0)
+
+/* used internally for ucg_cmd_help() and ucg_cmd_complete() */
+struct cmd_preparse {
+       int nb_valid_tok; /* number of valid tokens in the buffer */
+       void *opaque;     /* pointer to opaque data */
+       char comp_tok_buf[UCG_CMD_MAX_TOKEN_SIZE]; /* token to complete */
+       size_t comp_tok_len;    /* length of the token to complete */
+       size_t comp_tok_offset; /* offset of token to complete in the line buf */
+};
+
+/* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
+ * own. */
+static int
+isblank2(char c)
+{
+       if (c == ' ' || c == '\t' )
+               return 1;
+       return 0;
+}
+
+static int
+isendofline(char c)
+{
+       if (c == '\n' || c == '\r' )
+               return 1;
+       return 0;
+}
+
+static int
+iscomment(char c)
+{
+       if (c == '#')
+               return 1;
+       return 0;
+}
+
+int
+ucg_cmd_isendoftoken(char c)
+{
+       if (!c || iscomment(c) || isblank2(c) || isendofline(c))
+               return 1;
+       return 0;
+}
+
+static unsigned int
+nb_common_chars(const char * s1, const char * s2)
+{
+       unsigned int i=0;
+
+       while (*s1==*s2 && *s1 && *s2) {
+               s1++;
+               s2++;
+               i++;
+       }
+       return i;
+}
+
+/* quote a string and escape original quotes */
+int ucg_cmd_quote_token(char *dst, unsigned dstlen, const char *src)
+{
+       unsigned s = 0, d = 0;
+
+       /* the 2 quotes + '\0' */
+       if (dstlen < 3)
+               return -EMSGSIZE;
+
+       dst[d++] = '"';
+       while (src[s] != '\0') {
+               if (d >= (dstlen-2))
+                       return -EMSGSIZE;
+
+               if (src[s] == '"')
+                       dst[d++] = '\\';
+               if (src[s] == '\\' && src[s+1] == '"')
+                       dst[d++] = '\\';
+
+               dst[d++] = src[s++];
+       }
+
+       if (d >= (dstlen-2))
+               return -EMSGSIZE;
+       dst[d++] = '"';
+       dst[d++] = '\0';
+       return 0;
+}
+
+/* Remove quotes and stop when we reach the end of token. Return the
+ * number of "eaten" bytes from the source buffer, or a negative value
+ * on error */
+int ucg_cmd_get_token(char *dst, unsigned dstlen, const char *src)
+{
+       unsigned s = 0, d = 0;
+       int quoted = 0;
+
+       /* skip spaces */
+       while (isblank2(src[s]))
+               s++;
+
+       /* empty token */
+       if (ucg_cmd_isendoftoken(src[s]))
+               return -EINVAL;
+
+       /* copy token and remove quotes */
+       while (src[s] != '\0') {
+               if (d >= dstlen)
+                       return -EMSGSIZE;
+
+               if (ucg_cmd_isendoftoken(src[s]) && quoted == 0)
+                       break;
+
+               if (src[s] == '\\' && src[s+1] == '"') {
+                       dst[d++] = '"';
+                       s += 2;
+                       continue;
+               }
+               if (src[s] == '\\' && src[s+1] == '\\') {
+                       dst[d++] = '\\';
+                       s += 2;
+                       continue;
+               }
+               if (src[s] == '"') {
+                       s++;
+                       quoted = !quoted;
+                       continue;
+               }
+               dst[d++] = src[s++];
+       }
+
+       /* not enough room in dst buffer */
+       if (d >= (dstlen-1))
+               return -EMSGSIZE;
+
+       /* end of string during quote */
+       if (quoted)
+               return -EINVAL;
+
+       dst[d++] = '\0';
+       return  s;
+}
+
+/* return the nth token from src and copy it in dst. Return the offset
+ * of the token in src, or a negative value on error. Note: the index
+ * of the first token is 0. */
+static int cmd_get_nth_token(char *dst, unsigned dstlen, int n,
+       const char *src)
+{
+       int ret = 0, offset = 0;
+
+       do {
+               offset += ret;
+
+               /* skip spaces */
+               while (isblank2(src[offset]))
+                       offset++;
+
+               /* get the token starting at offset */
+               ret = ucg_cmd_get_token(dst, dstlen, src + offset);
+               if (ret < 0)
+                       return ret;
+
+       } while (n--);
+
+       return offset;
+}
+
+/*
+ * try to match the buffer with an instruction (only the first
+ * nb_match_token tokens if != 0).
+ *
+ * Return 0 if we match all the tokens, else the number of matched
+ * tokens, or a negative value on error.
+*/
+static int
+match_inst(const ucg_cmd_inst_t *inst, const char *linebuf,
+       unsigned int nb_match_token, void *resbuf, unsigned resbuf_size)
+{
+       unsigned int token_num = 0;
+       ucg_cmd_tk_hdr_t *token;
+       int n = 0, res;
+       char token_str[UCG_CMD_MAX_TOKEN_SIZE];
+
+       token = inst->tokens[token_num];
+
+       /* check if we match all tokens of inst */
+       while (token) {
+
+               /* we matched enough tokens, return success */
+               if (nb_match_token != 0 && token_num >= nb_match_token)
+                       return 0;
+
+               debug_printf("TK\n");
+
+               /* copy token and remove quotes */
+               n = ucg_cmd_get_token(token_str, sizeof(token_str), linebuf);
+               if (n < 0)
+                       break;
+
+               /* parse this token */
+               if (resbuf == NULL)
+                       res = token->ops->parse(token, token_str, NULL, 0);
+               else {
+                       unsigned rb_sz;
+                       void *rb = (char *)resbuf + token->offset;
+
+                       /* not enough room to store result */
+                       if (token->offset > resbuf_size)
+                               return -ENOBUFS;
+
+                       rb_sz = resbuf_size - token->offset;
+                       res = token->ops->parse(token, token_str, rb, rb_sz);
+               }
+
+               /* does not match this token */
+               if (res < 0)
+                       break;
+
+               debug_printf("TK parsed (len=%d)\n", n);
+               linebuf += n;
+               token_num ++;
+               token = inst->tokens[token_num];
+       }
+
+       /* does not match */
+       if (token_num == 0)
+               return -ENOENT;
+
+       /* we don't match all the tokens */
+       if (token)
+               return token_num;
+
+       /* are there are some tokens more */
+       while (isblank2(*linebuf))
+               linebuf++;
+
+       /* end of buf, we match all inst  */
+       if (*linebuf == '\0' || isendofline(*linebuf) || iscomment(*linebuf))
+               return 0;
+
+       /* garbage after inst */
+       return token_num;
+}
+
+
+/* Check if a line buffer is valid and can be parsed or completed. The
+ * parsing stops when \n or \0 is reached. The also function checks
+ * that tokens are correctly quoted. The number of tokens in the
+ * buffer is returned. */
+static int validate_linebuf(const char *linebuf)
+{
+       int quoted = 0, comment = 0;
+       int i = 0, nbtok = 0, token = 0;
+
+       while (linebuf[i] != '\0') {
+               if (isendofline(linebuf[i]) && quoted == 0)
+                       break;
+               if (comment == 1) {
+                       i++;
+                       continue;
+               }
+               if (iscomment(linebuf[i]) && quoted == 0) {
+                       comment = 1;
+                       i ++;
+                       continue;
+               }
+
+               /* end of token */
+               if (isblank2(linebuf[i]) && quoted == 0)
+                       token = 0;
+               /* new token */
+               if (!isblank2(linebuf[i]) && token == 0) {
+                       token = 1;
+                       nbtok++;
+               }
+
+               if (linebuf[i] == '\\' && linebuf[i+1] == '"') {
+                       i += 2;
+                       continue;
+               }
+               if (linebuf[i] == '\\' && linebuf[i+1] == '\\') {
+                       i += 2;
+                       continue;
+               }
+               if (linebuf[i] == '"') {
+                       i++;
+                       quoted = !quoted;
+                       continue;
+               }
+               i++;
+       }
+       if (quoted)
+               return UCG_CMD_PARSE_UNTERMINATED_QUOTE;
+       return nbtok;
+}
+
+/* Try to parse a buffer according to the specified context. The
+ * argument linebuf must end with \n or \0. */
+int
+ucg_cmd_parse(struct ucg_cmd *cl, const char *linebuf,
+       void *opaque)
+{
+       ucg_cmd_ctx_t *ctx = cl->ctx;
+       const ucg_cmd_inst_t **pinst;
+       const ucg_cmd_inst_t *inst;
+       char result_buf[UCG_CMD_MAX_DSTBUF_SIZE];
+       void (*f)(void *, struct ucg_cmd *, void *) = NULL;
+       void *data = NULL;
+       int ret;
+
+       ret = validate_linebuf(linebuf);
+       if (ret < 0)
+               return ret;
+       if (ret == 0)
+               return UCG_CMD_PARSE_EMPTY;
+
+
+       /* parse it !! */
+       for (pinst = &ctx->insts[0]; *pinst != NULL; pinst++) {
+               inst = *pinst;
+               debug_printf("INST\n");
+
+               /* fully parsed */
+               ret = match_inst(inst, linebuf, 0, result_buf,
+                       sizeof(result_buf));
+
+               if (ret != 0)
+                       continue;
+
+               debug_printf("INST fully parsed\n");
+
+               /* if end of buf -> there is no garbage after inst */
+               if (f != NULL) {
+                       /* more than 1 inst matches */
+                       debug_printf("Ambiguous cmd\n");
+                       return UCG_CMD_PARSE_AMBIGUOUS;
+               }
+               f = inst->f;
+               data = inst->data;
+       }
+
+       /* call func */
+       if (f == NULL)
+               return UCG_CMD_PARSE_NOMATCH;
+
+       f(result_buf, opaque, data);
+       return UCG_CMD_PARSE_SUCCESS;
+}
+
+/* called by ucg_cmd_help() and ucg_cmd_complete() to preparse the
+ * line buffer (the operations done are common to these functions) */
+static int cmd_preparse(struct cmd_preparse *preparse, const char *buf)
+{
+       int ret, len, nb_tok;
+
+       /* count the number of tokens in the line buffer */
+       ret = validate_linebuf(buf);
+       if (ret < 0)
+               return ret;
+       nb_tok = ret;
+
+       /* if last token is not complete, decrement nb_valid_tok */
+       len = strlen(buf);
+       if (nb_tok == 0 || isblank2(buf[len - 1])) {
+               preparse->nb_valid_tok = nb_tok;
+               preparse->comp_tok_offset = len;
+               preparse->comp_tok_buf[0] = '\0';
+               preparse->comp_tok_len = 0;
+       }
+       else {
+               preparse->nb_valid_tok = nb_tok - 1;
+
+               /* get the incomplete token (can be empty) and return its
+                * offset in the buffer */
+               preparse->comp_tok_offset =
+                       cmd_get_nth_token(preparse->comp_tok_buf,
+                               sizeof(preparse->comp_tok_buf),
+                               preparse->nb_valid_tok,
+                               buf);
+               preparse->comp_tok_len = strlen(preparse->comp_tok_buf);
+       }
+
+       return 0;
+}
+
+/* Display a contextual help in the command line. The contextual help
+ * depends on the buffer given. */
+void ucg_cmd_help(struct ucg_cmd *cl, const char *buf)
+{
+       ucg_cmd_ctx_t *ctx = cl->ctx;
+       ucg_cmd_tk_hdr_t *token;
+       const ucg_cmd_inst_t **pinst;
+       const ucg_cmd_inst_t *inst;
+       struct cmd_preparse preparse;
+       char tmpbuf[UCG_CMD_MAX_DSTBUF_SIZE];
+       char *help_str;
+       size_t n;
+       int iterate;
+
+       cmd_preparse(&preparse, buf);
+
+       debug_printf("display contextual help\n");
+
+       for (pinst = &ctx->insts[0]; *pinst != NULL; pinst++) {
+               inst = *pinst;
+
+               /* get instruction static help string */
+               help_str = inst->help_str;
+               if (help_str == NULL)
+                       help_str = "No help";
+
+               /* match the beginning of the command */
+               if (preparse.nb_valid_tok != 0 &&
+                       match_inst(inst, buf, preparse.nb_valid_tok,
+                               NULL, 0) != 0)
+                       continue;
+
+               token = inst->tokens[preparse.nb_valid_tok];
+
+               /* end of inst */
+               if (token == NULL) {
+                       ucg_cmd_printf(cl, "%-20s %s\n", "<Return>", help_str);
+                       continue;
+               }
+
+               /* token matches, but no completion */
+               if (token->ops->complete_start == NULL ||
+                       token->ops->complete_iterate == NULL)
+                       iterate = 0;
+               else
+                       iterate = 1;
+
+               /* store the incomplete token in tmpbuf */
+               n = preparse.comp_tok_len + 1;
+               if (n > sizeof(tmpbuf))
+                       n = sizeof(tmpbuf);
+               snprintf(tmpbuf, sizeof(tmpbuf), "%s", preparse.comp_tok_buf);
+
+               if (iterate == 1 &&
+                       token->ops->complete_start(token, tmpbuf,
+                               &preparse.opaque) < 0) {
+                       /* cancel iteration, complete_start() returned
+                        * a negative value, meaning no completion  */
+                       iterate = 0;
+                       if (token->ops->complete_end != NULL)
+                               token->ops->complete_end(token,
+                                       &preparse.opaque);
+               }
+
+               debug_printf("   iterate = %d\n", iterate);
+
+               if (iterate == 0) {
+                       /* get token dynamic help string */
+                       if ((token->ops->help == NULL) ||
+                               (token->ops->help(token, tmpbuf,
+                                       sizeof(tmpbuf)) < 0))
+                               snprintf(tmpbuf, sizeof(tmpbuf), "unknown");
+
+                       ucg_cmd_printf(cl, "%-20s %s\n", tmpbuf, help_str);
+                       continue;
+               }
+
+               /* iterate over all possible completion for this inst */
+               while (token->ops->complete_iterate(token,
+                               &preparse.opaque,
+                               tmpbuf,
+                               sizeof(tmpbuf)) >= 0) {
+
+
+                       debug_printf("   choice <%s>\n", tmpbuf);
+
+                       /* get the token and add it in help buffer */
+                       ucg_cmd_printf(cl, "%-20s %s\n", tmpbuf,
+                               iterate != 0? help_str : "''");
+
+                       /* don't display help next time */
+                       iterate = 0;
+               }
+
+               /* no more completion, go to next inst */
+               if (token->ops->complete_end != NULL)
+                       token->ops->complete_end(token, &preparse.opaque);
+       }
+}
+
+/* try to complete the buffer given as a parameter */
+int
+ucg_cmd_complete(struct ucg_cmd *cl, const char *buf,
+       char *dst, unsigned int dstsize)
+{
+       ucg_cmd_ctx_t *ctx = cl->ctx;
+       ucg_cmd_tk_hdr_t *token;
+       const ucg_cmd_inst_t **pinst;
+       const ucg_cmd_inst_t *inst;
+       struct cmd_preparse preparse;
+       int nb_match = 0;
+       int nb_completion = 0;
+       char completion_buf[UCG_CMD_MAX_TOKEN_SIZE];
+       char tmpbuf[UCG_CMD_MAX_TOKEN_SIZE];
+       int ret;
+       size_t n, completion_len = UCG_CMD_MAX_TOKEN_SIZE;
+
+       debug_printf("%s called\n", __FUNCTION__);
+
+       /* fill the preparse structure that contains infos that will
+        * help us to complete the buffer */
+       ret = cmd_preparse(&preparse, buf);
+       if (ret < 0)
+               return UCG_CMD_COMPLETE_NONE;
+
+       /* try to complete ! */
+       for (pinst = &ctx->insts[0]; *pinst != NULL; pinst++) {
+               inst = *pinst;
+
+               debug_printf("INST\n");
+
+               /* try to match the first tokens */
+               if (preparse.nb_valid_tok != 0 &&
+                       match_inst(inst, buf, preparse.nb_valid_tok,
+                               NULL, 0) != 0)
+                       continue;
+
+               nb_match ++;
+               token = inst->tokens[preparse.nb_valid_tok];
+
+               /* non completable */
+               if (token == NULL ||
+                       token->ops->complete_start == NULL ||
+                       token->ops->complete_iterate == NULL)
+                       continue;
+
+               /* store the incomplete token in tmpbuf */
+               n = preparse.comp_tok_len + 1;
+               if (n > sizeof(tmpbuf))
+                       n = sizeof(tmpbuf);
+               snprintf(tmpbuf, n, "%s", preparse.comp_tok_buf);
+
+               /* non completable */
+               if (token->ops->complete_start(token, tmpbuf,
+                               &preparse.opaque) < 0) {
+                       if (token->ops->complete_end != NULL)
+                               token->ops->complete_end(token,
+                                       &preparse.opaque);
+                       continue;
+               }
+
+               /* all possible completion for this token */
+               while (1) {
+
+                       ret = token->ops->complete_iterate(token,
+                               &preparse.opaque,
+                               tmpbuf,
+                               sizeof(tmpbuf)-1);
+
+                       if (ret < 0)
+                               break;
+
+                       debug_printf("Completion %s\n", tmpbuf);
+
+                       /* we kept at least the room for one char */
+                       if (ret == 0)
+                               strcat(tmpbuf, " ");
+
+                       debug_printf("   choice <%s>\n", tmpbuf);
+
+                       /* does the completion match the beginning of
+                        * the word ? */
+                       if (strncmp(preparse.comp_tok_buf, tmpbuf,
+                                       preparse.comp_tok_len))
+                               continue;
+
+                       /* first one, save the buffer */
+                       if (nb_completion == 0) {
+                               completion_len = snprintf(completion_buf,
+                                       sizeof(completion_buf),
+                                       "%s", tmpbuf);
+                       }
+                       else {
+                               n = nb_common_chars(completion_buf, tmpbuf);
+                               if (n < completion_len)
+                                       completion_len = n;
+                       }
+                       nb_completion ++;
+
+                       /* we cannot add any char, just display help */
+                       if (completion_len == preparse.comp_tok_len)
+                               break;
+               }
+               if (token->ops->complete_end != NULL)
+                       token->ops->complete_end(token, &preparse.opaque);
+
+               if (completion_len == preparse.comp_tok_len)
+                       break;
+       }
+
+       debug_printf("nb_completion=%d, completion_len=%d\n",
+               (int)nb_completion, (int)completion_len);
+
+       /* one choice, append chars and return */
+       if (nb_completion == 1) {
+               snprintf(dst, dstsize, "%s",
+                       completion_buf + preparse.comp_tok_len);
+               return UCG_CMD_COMPLETE_APPEND;
+       }
+
+       /* many choices, but starting with same chars: append chars
+        * and return */
+       if (nb_completion != 0 && completion_len > preparse.comp_tok_len) {
+               if (completion_len >= dstsize)
+                       completion_len = dstsize - 1;
+               strncpy(dst, completion_buf + preparse.comp_tok_len,
+                       completion_len - preparse.comp_tok_len);
+               dst[completion_len - preparse.comp_tok_len] = '\0';
+               return UCG_CMD_COMPLETE_APPEND;
+       }
+
+       /* no match, nothing to do */
+       if (nb_match == 0 || nb_completion == 0)
+               return UCG_CMD_COMPLETE_NONE;
+
+       return UCG_CMD_COMPLETE_MANY;
+}
+
diff --git a/lib/cmd/ucg_cmd_parse_etheraddr.c b/lib/cmd/ucg_cmd_parse_etheraddr.c
new file mode 100644 (file)
index 0000000..fd31084
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <net/ethernet.h>
+
+#include "ucg_cmd_parse.h"
+#include "ucg_cmd_parse_etheraddr.h"
+
+#define ETHER_ADDRSTRLEN 18
+
+#ifdef __linux__
+#define ea_oct ether_addr_octet
+#else
+#define ea_oct octet
+#endif
+
+static struct ether_addr *
+my_ether_aton(const char *a)
+{
+       int i;
+       static struct ether_addr ether_addr;
+       unsigned int o0, o1, o2, o3, o4, o5;
+
+       i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o0, &o1, &o2, &o3, &o4, &o5);
+
+       if (i != ETHER_ADDR_LEN)
+               return NULL;
+
+       ether_addr.ea_oct[0] = (uint8_t)o0;
+       ether_addr.ea_oct[1] = (uint8_t)o1;
+       ether_addr.ea_oct[2] = (uint8_t)o2;
+       ether_addr.ea_oct[3] = (uint8_t)o3;
+       ether_addr.ea_oct[4] = (uint8_t)o4;
+       ether_addr.ea_oct[5] = (uint8_t)o5;
+
+       return (struct ether_addr *)&ether_addr;
+}
+
+static int
+cmd_parse_etheraddr(
+       __attribute__((unused)) ucg_cmd_tk_hdr_t *tk,
+       const char *buf, void *res, unsigned ressize)
+{
+       unsigned int token_len = 0;
+       char ether_str[ETHER_ADDRSTRLEN];
+       struct ether_addr *tmp;
+
+       if (res && ressize < sizeof(struct ether_addr))
+               return -1;
+
+       /* if token is too big... */
+       token_len = snprintf(ether_str, sizeof(ether_str), "%s", buf);
+       if (token_len >= sizeof(ether_str))
+               return -1;
+
+       tmp = my_ether_aton(ether_str);
+       if (tmp == NULL)
+               return -1;
+
+       if (res != NULL)
+               memcpy(res, tmp, sizeof(struct ether_addr));
+       return 0;
+}
+
+static int
+cmd_help_etheraddr(
+       __attribute__((unused)) ucg_cmd_tk_hdr_t *tk,
+       char *dstbuf, unsigned int size)
+{
+       snprintf(dstbuf, size, "<ether addr>");
+       return 0;
+}
+
+struct ucg_cmd_tk_ops ucg_cmd_tk_etheraddr_ops = {
+       .parse = cmd_parse_etheraddr,
+       .complete_start = NULL,
+       .complete_iterate = NULL,
+       .complete_end = NULL,
+       .help = cmd_help_etheraddr,
+};
diff --git a/lib/cmd/ucg_cmd_parse_file.c b/lib/cmd/ucg_cmd_parse_file.c
new file mode 100644 (file)
index 0000000..44917af
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <dirent.h>
+
+#include "ucg_cmd_parse.h"
+#include "ucg_cmd_parse_file.h"
+
+struct cmd_complete_file_callback {
+       char *token;
+       DIR *dir;
+};
+
+static int
+cmd_parse_file(ucg_cmd_tk_hdr_t *tk, const char *buf,
+       void *res, unsigned ressize)
+{
+       struct ucg_cmd_tk_file *tk2 = (struct ucg_cmd_tk_file *)tk;
+       struct ucg_cmd_tk_file_data *sd = &tk2->file_data;;
+       unsigned int token_len;
+       struct stat st;
+       int flags, ret;
+       char *tmp, *dname;
+
+       if (res && ressize < UCG_FILENAME_SIZE)
+               return -1;
+
+       token_len = strlen(buf);
+       if (token_len >= (UCG_FILENAME_SIZE - 1) || token_len == 0)
+               return -1;
+
+       flags = sd->flags;
+
+       if (flags & PARSE_FILE_F_CREATE) {
+               /* the directory must exist */
+               tmp = strdup(buf);
+               dname = dirname(tmp);
+               ret = stat(dname, &st);
+               if (ret != 0)
+                       ret = lstat(dname, &st);
+               free(tmp);
+               if (ret != 0)
+                       return -1;
+               if (!S_ISDIR(st.st_mode))
+                       return -1;
+       }
+       else {
+               ret = stat(buf, &st);
+               if (ret != 0)
+                       return -1;
+               if (flags & PARSE_FILE_F_DIRECTORY)
+                       if (!S_ISDIR(st.st_mode))
+                               return -1;
+       }
+
+       /* we already checked that token_len is < FILENAME_SIZE-1 */
+       if (res)
+               strcpy(res, buf);
+
+       return 0;
+}
+
+/*
+ * This function is quite similar to dirname(3) except that:
+ * - it allocates the returned string and don't modify the argument
+ * - the result of dirname2() is not exactly the same than dirname()
+ * path          dirname2
+ * "/usr/lib"    "/usr"
+ * "/usr/"       "/usr"
+ * "/usr"        "/"
+ * "usr"         "." or "" if allow_empty == 1
+ * ""            "." or "" if allow_empty == 1
+ * "/"           "/"
+ * "."           "." or "" if allow_empty == 1
+ * "./"          "."
+ * ".."          "." or "" if allow_empty == 1
+ * "../"          ".."
+ */
+static char *
+dirname2(const char *path, int allow_empty)
+{
+       char *s;
+       char *last_slash;
+       int len;
+
+       len = strlen(path);
+       if (len == 0 && allow_empty == 0) {
+               s = strdup(".");
+               return s;
+       }
+
+       s = strdup(path);
+       last_slash = strrchr(s, '/');
+       if (last_slash == NULL) {
+               if (allow_empty)
+                       s[0] = '\0';
+               else
+                       strcpy(s, ".");
+       }
+       else if (last_slash == s)
+               s[1] = '\0';
+       else {
+               *last_slash = '\0';
+       }
+
+       return s;
+}
+
+static int
+cmd_complete_file_start(ucg_cmd_tk_hdr_t *tk,
+       const char *tokstr, void **opaque)
+{
+       char *dname;
+       struct cmd_complete_file_callback *cb;
+
+       (void)tk;
+       *opaque = NULL;
+       cb = malloc(sizeof(*cb));
+       if (cb == NULL)
+               return -1;
+       memset(cb, 0, sizeof(*cb));
+       *opaque = cb;
+
+       cb->token = strdup(tokstr);
+       /* we need to copy again tokstr because dirname() alters the string */
+       dname = dirname2(tokstr, 0);
+       cb->dir = opendir(dname);
+       free(dname);
+
+       if (cb->dir == NULL)
+               return -1;
+
+       return 0;
+}
+
+static int
+cmd_complete_file_iterate(ucg_cmd_tk_hdr_t *tk, void **opaque,
+       char *dstbuf, unsigned int size)
+{
+       struct ucg_cmd_tk_file *tk2 = (struct ucg_cmd_tk_file *)tk;
+       struct ucg_cmd_tk_file_data *sd = &tk2->file_data;
+       struct cmd_complete_file_callback *cb;
+       struct dirent *de;
+       struct stat st;
+       char *dname;
+       int len;
+       int need_join_slash = 1;
+       int flags = sd->flags;
+
+       cb = *opaque;
+       /* read next dir name, skipping "." and ".." */
+       while (1) {
+               de = readdir(cb->dir);
+               if (de == NULL)
+                       return -1;
+               if (strcmp(de->d_name, ".") == 0 ||
+                       strcmp(de->d_name, "..") == 0)
+                       continue;
+
+
+               /* do we need a / to join dirname and basename ? */
+               dname = dirname2(cb->token, 1);
+               len = strlen(dname);
+               if (len == 0 || dname[len - 1] == '/')
+                       need_join_slash = 0;
+
+               /* keep one byte for potential '/' */
+               len = snprintf(dstbuf, size-1, "%s%s%s", dname,
+                       need_join_slash ? "/" : "", de->d_name);
+               free(dname);
+               if (len < 0 || len >= (int)size - 1)
+                       continue;
+
+               /* append '/' if it's a directory */
+               if (stat(dstbuf, &st) != 0) {
+                       if (lstat(dstbuf, &st) != 0)
+                               return -1;
+               }
+               if (S_ISDIR(st.st_mode)) {
+                       strcat(dstbuf, "/");
+                       return 1; /* intermediate completion */
+               }
+               /* skip non-directories */
+               else if (flags & PARSE_FILE_F_DIRECTORY)
+                       continue;
+               break;
+       }
+
+       return 0;
+}
+
+static void
+cmd_complete_file_end(ucg_cmd_tk_hdr_t *tk, void **opaque)
+{
+       struct cmd_complete_file_callback *cb;
+
+       (void)tk;
+       cb = *opaque;
+
+       if (cb == NULL)
+               return;
+
+       if (cb->dir)
+               closedir(cb->dir);
+       if (cb->token)
+               free(cb->token);
+
+       free(cb);
+}
+
+
+static int
+cmd_help_file(ucg_cmd_tk_hdr_t *tk, char *dstbuf,
+       unsigned int size)
+{
+       struct ucg_cmd_tk_file *tk2 = (struct ucg_cmd_tk_file *)tk;
+       struct ucg_cmd_tk_file_data *sd = &tk2->file_data;;
+       int flags;
+
+       flags = sd->flags;
+       if (flags & PARSE_FILE_F_DIRECTORY)
+               snprintf(dstbuf, size, "<dir>");
+       else
+               snprintf(dstbuf, size, "<file>");
+
+       return 0;
+}
+
+struct ucg_cmd_tk_ops ucg_cmd_tk_file_ops = {
+       .parse = cmd_parse_file,
+       .complete_start = cmd_complete_file_start,
+       .complete_iterate = cmd_complete_file_iterate,
+       .complete_end = cmd_complete_file_end,
+       .help = cmd_help_file,
+};
diff --git a/lib/cmd/ucg_cmd_parse_ipaddr.c b/lib/cmd/ucg_cmd_parse_ipaddr.c
new file mode 100644 (file)
index 0000000..55aa7c1
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For inet_ntop() functions:
+ *
+ * Copyright (c) 1996 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#ifndef __linux__
+#include <net/socket.h>
+#endif
+
+#include <ucg_cmd_parse.h>
+#include <ucg_cmd_parse_ipaddr.h>
+
+#define INADDRSZ 4
+#define IN6ADDRSZ 16
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static int inet_pton4(const char *src, unsigned char *dst);
+static int inet_pton6(const char *src, unsigned char *dst);
+
+/* int
+ * inet_pton(af, src, dst)
+ *      convert from presentation format (which usually means ASCII printable)
+ *      to network format (which is usually some kind of binary format).
+ * return:
+ *      1 if the address was valid for the specified address family
+ *      0 if the address wasn't valid (`dst' is untouched in this case)
+ *      -1 if some other error occurred (`dst' is untouched in this case, too)
+ * author:
+ *      Paul Vixie, 1996.
+ */
+static int
+my_inet_pton(int af, const char *src, void *dst)
+{
+       switch (af) {
+               case AF_INET:
+                       return (inet_pton4(src, dst));
+               case AF_INET6:
+                       return (inet_pton6(src, dst));
+               default:
+                       errno = EAFNOSUPPORT;
+                       return (-1);
+       }
+       /* NOTREACHED */
+}
+
+/* int
+ * inet_pton4(src, dst)
+ *      like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ *      1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ *      does not touch `dst' unless it's returning 1.
+ * author:
+ *      Paul Vixie, 1996.
+ */
+static int
+inet_pton4(const char *src, unsigned char *dst)
+{
+       static const char digits[] = "0123456789";
+       int saw_digit, octets, ch;
+       unsigned char tmp[INADDRSZ], *tp;
+
+       saw_digit = 0;
+       octets = 0;
+       *(tp = tmp) = 0;
+       while ((ch = *src++) != '\0') {
+               const char *pch;
+
+               if ((pch = strchr(digits, ch)) != NULL) {
+                       unsigned int new = *tp * 10 + (pch - digits);
+
+                       if (new > 255)
+                               return (0);
+                       if (! saw_digit) {
+                               if (++octets > 4)
+                                       return (0);
+                               saw_digit = 1;
+                       }
+                       *tp = (unsigned char)new;
+               } else if (ch == '.' && saw_digit) {
+                       if (octets == 4)
+                               return (0);
+                       *++tp = 0;
+                       saw_digit = 0;
+               } else
+                       return (0);
+       }
+       if (octets < 4)
+               return (0);
+
+       memcpy(dst, tmp, INADDRSZ);
+       return (1);
+}
+
+/* int
+ * inet_pton6(src, dst)
+ *      convert presentation level address to network order binary form.
+ * return:
+ *      1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ *      (1) does not touch `dst' unless it's returning 1.
+ *      (2) :: in a full address is silently ignored.
+ * credit:
+ *      inspired by Mark Andrews.
+ * author:
+ *      Paul Vixie, 1996.
+ */
+static int
+inet_pton6(const char *src, unsigned char *dst)
+{
+       static const char xdigits_l[] = "0123456789abcdef",
+               xdigits_u[] = "0123456789ABCDEF";
+       unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
+       const char *xdigits, *curtok;
+       int ch, saw_xdigit, count_xdigit;
+       unsigned int val;
+
+       memset((tp = tmp), '\0', IN6ADDRSZ);
+       endp = tp + IN6ADDRSZ;
+       colonp = NULL;
+       /* Leading :: requires some special handling. */
+       if (*src == ':')
+               if (*++src != ':')
+                       return (0);
+       curtok = src;
+       saw_xdigit = count_xdigit = 0;
+       val = 0;
+       while ((ch = *src++) != '\0') {
+               const char *pch;
+
+               if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+                       pch = strchr((xdigits = xdigits_u), ch);
+               if (pch != NULL) {
+                       if (count_xdigit >= 4)
+                               return (0);
+                       val <<= 4;
+                       val |= (pch - xdigits);
+                       if (val > 0xffff)
+                               return (0);
+                       saw_xdigit = 1;
+                       count_xdigit++;
+                       continue;
+               }
+               if (ch == ':') {
+                       curtok = src;
+                       if (!saw_xdigit) {
+                               if (colonp)
+                                       return (0);
+                               colonp = tp;
+                               continue;
+                       } else if (*src == '\0') {
+                               return (0);
+                       }
+                       if (tp + sizeof(int16_t) > endp)
+                               return (0);
+                       *tp++ = (unsigned char) ((val >> 8) & 0xff);
+                       *tp++ = (unsigned char) (val & 0xff);
+                       saw_xdigit = 0;
+                       count_xdigit = 0;
+                       val = 0;
+                       continue;
+               }
+               if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
+                   inet_pton4(curtok, tp) > 0) {
+                       tp += INADDRSZ;
+                       saw_xdigit = 0;
+                       count_xdigit = 0;
+                       break;  /* '\0' was seen by inet_pton4(). */
+               }
+               return (0);
+       }
+       if (saw_xdigit) {
+               if (tp + sizeof(int16_t) > endp)
+                       return (0);
+               *tp++ = (unsigned char) ((val >> 8) & 0xff);
+               *tp++ = (unsigned char) (val & 0xff);
+       }
+       if (colonp != NULL) {
+               /*
+                * Since some memmove()'s erroneously fail to handle
+                * overlapping regions, we'll do the shift by hand.
+                */
+               const int n = tp - colonp;
+               int i;
+
+               for (i = 1; i <= n; i++) {
+                       endp[- i] = colonp[n - i];
+                       colonp[n - i] = 0;
+               }
+               tp = endp;
+       }
+       if (tp != endp)
+               return (0);
+       memcpy(dst, tmp, IN6ADDRSZ);
+       return (1);
+}
+
+static int
+cmd_parse_ipaddr(ucg_cmd_tk_hdr_t *tk, const char *buf,
+       void *res, unsigned ressize)
+{
+       struct ucg_cmd_tk_ipaddr *tk2 =
+               (struct ucg_cmd_tk_ipaddr *)tk;
+       unsigned int token_len = 0;
+       char ip_str[INET6_ADDRSTRLEN+4]; /* '+4' is for prefixlen (if any) */
+       ucg_cmd_ipaddr_t ipaddr;
+       char *prefix, *prefix_end;
+       long prefixlen;
+
+       if (res && ressize < sizeof(ucg_cmd_ipaddr_t))
+               return -1;
+
+       memset(&ipaddr, 0, sizeof(ipaddr));
+
+       /* if token is too big... */
+       token_len = snprintf(ip_str, sizeof(ip_str), "%s", buf);
+       if (token_len >= sizeof(ip_str))
+               return -1;
+
+       /* convert the network prefix */
+       if (tk2->ipaddr_data.flags & UCG_CMD_IPADDR_NETWORK) {
+               prefix = strrchr(ip_str, '/');
+               if (prefix == NULL)
+                       return -1;
+               *prefix = '\0';
+               prefix ++;
+               errno = 0;
+               prefixlen = strtol(prefix, &prefix_end, 10);
+               if (errno || (*prefix_end != '\0') )
+                       return -1;
+               ipaddr.prefixlen = prefixlen;
+       }
+       else {
+               ipaddr.prefixlen = 0;
+       }
+
+       /* convert the IP addr */
+       if ((tk2->ipaddr_data.flags & UCG_CMD_IPADDR_V4) &&
+           my_inet_pton(AF_INET, ip_str, &ipaddr.addr.ipv4) == 1) {
+               ipaddr.family = AF_INET;
+               if (res != NULL)
+                       memcpy(res, &ipaddr, sizeof(ipaddr));
+               return 0;
+       }
+       if ((tk2->ipaddr_data.flags & UCG_CMD_IPADDR_V6) &&
+           my_inet_pton(AF_INET6, ip_str, &ipaddr.addr.ipv6) == 1) {
+               ipaddr.family = AF_INET6;
+               if (res != NULL)
+                       memcpy(res, &ipaddr, sizeof(ipaddr));
+               return 0;
+       }
+       return -1;
+
+}
+
+static int
+cmd_help_ipaddr(ucg_cmd_tk_hdr_t *tk, char *dstbuf,
+                   unsigned int size)
+{
+       struct ucg_cmd_tk_ipaddr *tk2 =
+               (struct ucg_cmd_tk_ipaddr *)tk;
+
+       switch (tk2->ipaddr_data.flags) {
+       case UCG_CMD_IPADDR_V4:
+               snprintf(dstbuf, size, "<IPv4>");
+               break;
+       case UCG_CMD_IPADDR_V6:
+               snprintf(dstbuf, size, "<IPv6>");
+               break;
+       case UCG_CMD_IPADDR_V4 | UCG_CMD_IPADDR_V6:
+               snprintf(dstbuf, size, "<IPv4/IPv6>");
+               break;
+       case UCG_CMD_IPADDR_NETWORK | UCG_CMD_IPADDR_V4:
+               snprintf(dstbuf, size, "<IPv4 network>");
+               break;
+       case UCG_CMD_IPADDR_NETWORK | UCG_CMD_IPADDR_V6:
+               snprintf(dstbuf, size, "<IPv6 network>");
+               break;
+       case UCG_CMD_IPADDR_NETWORK | UCG_CMD_IPADDR_V4 |
+               UCG_CMD_IPADDR_V6:
+               snprintf(dstbuf, size, "<IPv4/IPv6 network>");
+               break;
+       default:
+               snprintf(dstbuf, size, "<IPaddr (bad flags)>");
+               break;
+       }
+       return 0;
+}
+
+struct ucg_cmd_tk_ops cmd_token_ipaddr_ops = {
+       .parse = cmd_parse_ipaddr,
+       .complete_start = NULL,
+       .complete_iterate = NULL,
+       .complete_end = NULL,
+       .help = cmd_help_ipaddr,
+};
+
diff --git a/lib/cmd/ucg_cmd_parse_num.c b/lib/cmd/ucg_cmd_parse_num.c
new file mode 100644 (file)
index 0000000..7de21c2
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "ucg_cmd_parse.h"
+#include "ucg_cmd_parse_num.h"
+
+//#define debug_printf(args...) printf(args)
+#define debug_printf(args...) do {} while(0)
+
+enum num_parse_state_t {
+       START,
+       DEC_NEG,
+       BIN,
+       HEX,
+       FLOAT_POS,
+       FLOAT_NEG,
+
+       ERROR,
+
+       FIRST_OK, /* not used */
+       ZERO_OK,
+       HEX_OK,
+       OCTAL_OK,
+       BIN_OK,
+       DEC_NEG_OK,
+       DEC_POS_OK,
+       FLOAT_POS_OK,
+       FLOAT_NEG_OK
+};
+
+/* Keep it sync with enum in .h */
+static const char * num_help[] = {
+       "<uint8>", "<uint16>", "<uint32>", "<uint64>",
+       "<int8>", "<int16>", "<int32>", "<int64>",
+#ifdef UCG_CMD_HAVE_FLOAT
+       "<float>",
+#endif
+};
+
+static int
+add_to_res(unsigned int c, uint64_t *res, unsigned int base)
+{
+       /* overflow */
+       if ( (UINT64_MAX - c) / base < *res ) {
+               return -1;
+       }
+
+       *res = (uint64_t) (*res * base + c);
+       return 0;
+}
+
+static int
+check_res_size(struct ucg_cmd_tk_num_data *nd, unsigned ressize)
+{
+       switch (nd->type) {
+       case INT8:
+       case UINT8:
+               if (ressize < sizeof(int8_t))
+                       return -1;
+               break;
+       case INT16:
+       case UINT16:
+               if (ressize < sizeof(int16_t))
+                       return -1;
+               break;
+       case INT32:
+       case UINT32:
+               if (ressize < sizeof(int32_t))
+                       return -1;
+               break;
+       case INT64:
+       case UINT64:
+               if (ressize < sizeof(int64_t))
+                       return -1;
+               break;
+#ifdef UCG_CMD_HAVE_FLOAT
+       case FLOAT:
+               if (ressize < sizeof(float))
+                       return -1;
+               break;
+#endif
+       default:
+               return -1;
+       }
+       return 0;
+}
+
+/* parse an int or a float */
+static int
+cmd_parse_num(ucg_cmd_tk_hdr_t *tk, const char *srcbuf,
+       void *res, unsigned ressize)
+{
+       struct ucg_cmd_tk_num_data nd;
+       enum num_parse_state_t st = START;
+       const char * buf = srcbuf;
+       char c = *buf;
+       uint64_t res1 = 0;
+#ifdef UCG_CMD_HAVE_FLOAT
+       uint64_t res2 = 0, res3 = 1;
+#endif
+
+       memcpy(&nd, &((struct ucg_cmd_tk_num *)tk)->num_data, sizeof(nd));
+
+       /* check that we have enough room in res */
+       if (res) {
+               if (check_res_size(&nd, ressize) < 0)
+                       return -1;
+       }
+
+       while (st != ERROR && c != '\0') {
+               debug_printf("%c %x -> ", c, c);
+               switch (st) {
+               case START:
+                       if (c == '-') {
+                               st = DEC_NEG;
+                       }
+                       else if (c == '0') {
+                               st = ZERO_OK;
+                       }
+#ifdef UCG_CMD_HAVE_FLOAT
+                       else if (c == '.') {
+                               st = FLOAT_POS;
+                               res1 = 0;
+                       }
+#endif
+                       else if (c >= '1' && c <= '9') {
+                               if (add_to_res(c - '0', &res1, 10) < 0)
+                                       st = ERROR;
+                               else
+                                       st = DEC_POS_OK;
+                       }
+                       else  {
+                               st = ERROR;
+                       }
+                       break;
+
+               case ZERO_OK:
+                       if (c == 'x') {
+                               st = HEX;
+                       }
+                       else if (c == 'b') {
+                               st = BIN;
+                       }
+#ifdef UCG_CMD_HAVE_FLOAT
+                       else if (c == '.') {
+                               st = FLOAT_POS;
+                               res1 = 0;
+                       }
+#endif
+                       else if (c >= '0' && c <= '7') {
+                               if (add_to_res(c - '0', &res1, 10) < 0)
+                                       st = ERROR;
+                               else
+                                       st = OCTAL_OK;
+                       }
+                       else  {
+                               st = ERROR;
+                       }
+                       break;
+
+               case DEC_NEG:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res1, 10) < 0)
+                                       st = ERROR;
+                               else
+                                       st = DEC_NEG_OK;
+                       }
+#ifdef UCG_CMD_HAVE_FLOAT
+                       else if (c == '.') {
+                               res1 = 0;
+                               st = FLOAT_NEG;
+                       }
+#endif
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+               case DEC_NEG_OK:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res1, 10) < 0)
+                                       st = ERROR;
+                       }
+#ifdef UCG_CMD_HAVE_FLOAT
+                       else if (c == '.') {
+                               st = FLOAT_NEG;
+                       }
+#endif
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+               case DEC_POS_OK:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res1, 10) < 0)
+                                       st = ERROR;
+                       }
+#ifdef UCG_CMD_HAVE_FLOAT
+                       else if (c == '.') {
+                               st = FLOAT_POS;
+                       }
+#endif
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+               case HEX:
+                       st = HEX_OK;
+                       /* no break */
+               case HEX_OK:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res1, 16) < 0)
+                                       st = ERROR;
+                       }
+                       else if (c >= 'a' && c <= 'f') {
+                               if (add_to_res(c - 'a' + 10, &res1, 16) < 0)
+                                       st = ERROR;
+                       }
+                       else if (c >= 'A' && c <= 'F') {
+                               if (add_to_res(c - 'A' + 10, &res1, 16) < 0)
+                                       st = ERROR;
+                       }
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+
+               case OCTAL_OK:
+                       if (c >= '0' && c <= '7') {
+                               if (add_to_res(c - '0', &res1, 8) < 0)
+                                       st = ERROR;
+                       }
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+               case BIN:
+                       st = BIN_OK;
+                       /* no break */
+               case BIN_OK:
+                       if (c >= '0' && c <= '1') {
+                               if (add_to_res(c - '0', &res1, 2) < 0)
+                                       st = ERROR;
+                       }
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+#ifdef UCG_CMD_HAVE_FLOAT
+               case FLOAT_POS:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res2, 10) < 0)
+                                       st = ERROR;
+                               else
+                                       st = FLOAT_POS_OK;
+                               res3 = 10;
+                       }
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+               case FLOAT_NEG:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res2, 10) < 0)
+                                       st = ERROR;
+                               else
+                                       st = FLOAT_NEG_OK;
+                               res3 = 10;
+                       }
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+               case FLOAT_POS_OK:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res2, 10) < 0)
+                                       st = ERROR;
+                               if (add_to_res(0, &res3, 10) < 0)
+                                       st = ERROR;
+                       }
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+               case FLOAT_NEG_OK:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res2, 10) < 0)
+                                       st = ERROR;
+                               if (add_to_res(0, &res3, 10) < 0)
+                                       st = ERROR;
+                       }
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+#endif
+
+               default:
+                       debug_printf("not impl ");
+
+               }
+
+#ifdef UCG_CMD_HAVE_FLOAT
+               debug_printf("(%"PRIu32")  (%"PRIu32")  (%"PRIu32")\n",
+                       res1, res2, res3);
+#else
+               debug_printf("(%"PRIu32")\n", res1);
+#endif
+
+               buf ++;
+               c = *buf;
+
+               /* token too long */
+               if (buf-srcbuf > 127)
+                       return -1;
+       }
+
+       switch (st) {
+       case ZERO_OK:
+       case DEC_POS_OK:
+       case HEX_OK:
+       case OCTAL_OK:
+       case BIN_OK:
+               if ( nd.type == INT8 && res1 <= INT8_MAX ) {
+                       if (res)
+                               *(int8_t *)res = (int8_t) res1;
+                       return 0;
+               }
+               else if ( nd.type == INT16 && res1 <= INT16_MAX ) {
+                       if (res)
+                               *(int16_t *)res = (int16_t) res1;
+                       return 0;
+               }
+               else if ( nd.type == INT32 && res1 <= INT32_MAX ) {
+                       if (res)
+                               *(int32_t *)res = (int32_t) res1;
+                       return 0;
+               }
+               else if ( nd.type == UINT8 && res1 <= UINT8_MAX ) {
+                       if (res)
+                               *(uint8_t *)res = (uint8_t) res1;
+                       return 0;
+               }
+               else if (nd.type == UINT16  && res1 <= UINT16_MAX ) {
+                       if (res)
+                               *(uint16_t *)res = (uint16_t) res1;
+                       return 0;
+               }
+               else if ( nd.type == UINT32 ) {
+                       if (res)
+                               *(uint32_t *)res = (uint32_t) res1;
+                       return 0;
+               }
+               else if ( nd.type == UINT64 ) {
+                       if (res)
+                               *(uint64_t *)res = res1;
+                       return 0;
+               }
+#ifdef UCG_CMD_HAVE_FLOAT
+               else if ( nd.type == FLOAT ) {
+                       if (res)
+                               *(float *)res = (float)res1;
+                       return 0;
+               }
+#endif
+               else {
+                       return -1;
+               }
+               break;
+
+       case DEC_NEG_OK:
+               if ( nd.type == INT8 && res1 <= INT8_MAX + 1 ) {
+                       if (res)
+                               *(int8_t *)res = (int8_t) (-res1);
+                       return 0;
+               }
+               else if ( nd.type == INT16 && res1 <= (uint16_t)INT16_MAX + 1 ) {
+                       if (res)
+                               *(int16_t *)res = (int16_t) (-res1);
+                       return 0;
+               }
+               else if ( nd.type == INT32 && res1 <= (uint32_t)INT32_MAX + 1 ) {
+                       if (res)
+                               *(int32_t *)res = (int32_t) (-res1);
+                       return 0;
+               }
+#ifdef UCG_CMD_HAVE_FLOAT
+               else if ( nd.type == FLOAT ) {
+                       if (res)
+                               *(float *)res = - (float)res1;
+                       return 0;
+               }
+#endif
+               else {
+                       return -1;
+               }
+               break;
+
+#ifdef UCG_CMD_HAVE_FLOAT
+       case FLOAT_POS:
+       case FLOAT_POS_OK:
+               if ( nd.type == FLOAT ) {
+                       if (res)
+                               *(float *)res = (float)res1 +
+                                       ((float)res2 / (float)res3);
+                       return 0;
+
+               }
+               else {
+                       return -1;
+               }
+               break;
+
+       case FLOAT_NEG:
+       case FLOAT_NEG_OK:
+               if ( nd.type == FLOAT ) {
+                       if (res)
+                               *(float *)res = - ((float)res1 +
+((float)res2 / (float)res3));
+                       return 0;
+
+               }
+               else {
+                       return -1;
+               }
+               break;
+#endif
+       default:
+               debug_printf("error\n");
+               return -1;
+       }
+}
+
+
+/* parse an int or a float */
+static int
+cmd_help_num(ucg_cmd_tk_hdr_t *tk, char *dstbuf,
+       unsigned int size)
+{
+       struct ucg_cmd_tk_num_data nd;
+
+       memcpy(&nd, &((struct ucg_cmd_tk_num *)tk)->num_data, sizeof(nd));
+
+       /* should not happen.... don't so this test */
+       /* if (nd.type >= (sizeof(num_help)/sizeof(const char *))) */
+       /* return -1; */
+
+       strncpy(dstbuf, num_help[nd.type], size);
+       dstbuf[size-1] = '\0';
+       return 0;
+}
+
+
+struct ucg_cmd_tk_ops ucg_cmd_tk_num_ops = {
+       .parse = cmd_parse_num,
+       .complete_start = NULL,
+       .complete_iterate = NULL,
+       .complete_end = NULL,
+       .help = cmd_help_num,
+};
diff --git a/lib/cmd/ucg_cmd_parse_string.c b/lib/cmd/ucg_cmd_parse_string.c
new file mode 100644 (file)
index 0000000..35778c8
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "ucg_cmd_parse.h"
+#include "ucg_cmd_parse_string.h"
+
+#define MULTISTRING_HELP "Mul-choice STRING"
+#define ANYSTRING_HELP   "Any STRING"
+#define FIXEDSTRING_HELP "Fixed STRING"
+
+static unsigned int
+get_token_len(const char *s)
+{
+       char c;
+       unsigned int i=0;
+
+       c = s[i];
+       while (c!='#' && c!='\0') {
+               i++;
+               c = s[i];
+       }
+       return i;
+}
+
+static const char *
+get_next_token(const char *s)
+{
+       unsigned int i;
+       i = get_token_len(s);
+       if (s[i] == '#')
+               return s+i+1;
+       return NULL;
+}
+
+static int
+parse_fixed_string(struct ucg_cmd_tk_string_data *sd,
+       const char *buf, unsigned token_len)
+{
+       unsigned int conf_token_len;
+       const char *str;
+
+       str = sd->str;
+       for (str = sd->str; str != NULL ; str = get_next_token(str)) {
+
+               conf_token_len = get_token_len(str);
+
+               /* if token from config is too big... */
+               if (conf_token_len >= UCG_STR_TOKEN_SIZE - 1)
+                       continue;
+
+               /* compare conf token and user token */
+               if (token_len == conf_token_len &&
+                       strncmp(buf, str, token_len) == 0)
+                       return 0;
+       }
+
+       return -1;
+}
+
+static int
+cmd_parse_string(ucg_cmd_tk_hdr_t *tk,
+       const char *buf, void *res, unsigned ressize)
+{
+       struct ucg_cmd_tk_string *tk2 =
+               (struct ucg_cmd_tk_string *)tk;
+       struct ucg_cmd_tk_string_data *sd = &tk2->string_data;;
+       unsigned int token_len;
+
+       if (res && ressize < UCG_STR_TOKEN_SIZE)
+               return -1;
+
+       token_len = strlen(buf);
+
+       if (token_len >= (UCG_STR_TOKEN_SIZE - 1) || token_len == 0)
+               return -1;
+
+       /* fixed string */
+       if (sd->str) {
+               if (parse_fixed_string(sd, buf, token_len) < 0)
+                       return -1;
+       }
+
+       /* we already checked that token_len is < STR_TOKEN_SIZE-1 */
+       if (res)
+               strcpy(res, buf);
+
+       return 0;
+}
+
+static int
+cmd_complete_string_start(ucg_cmd_tk_hdr_t *tk,
+       __attribute__((unused)) const char *tokstr,
+       void **opaque)
+{
+       struct ucg_cmd_tk_string *tk2 =
+               (struct ucg_cmd_tk_string *)tk;
+       struct ucg_cmd_tk_string_data *sd = &tk2->string_data;;
+       const char *str;
+
+       str = sd->str;
+       *opaque = (void *)str;
+       if (str == NULL)
+               return -1; /* no completion */
+       return 0;
+}
+
+static int
+cmd_complete_string_iterate(ucg_cmd_tk_hdr_t *tk, void **opaque,
+       char *dstbuf, unsigned int size)
+{
+       const char *s;
+       unsigned int len;
+
+       (void)tk;
+       s = *opaque;
+       if (s == NULL)
+               return -1;
+       *opaque = (void *)get_next_token(s);
+
+       len = get_token_len(s);
+       if (len > size - 1)
+               return -1;
+
+       memcpy(dstbuf, s, len);
+       dstbuf[len] = '\0';
+       return 0;
+}
+
+static int
+cmd_help_string(ucg_cmd_tk_hdr_t *tk, char *dstbuf,
+       unsigned int size)
+{
+       (void)tk;
+       snprintf(dstbuf, size, "<string>");
+       return 0;
+}
+
+struct ucg_cmd_tk_ops ucg_cmd_tk_string_ops = {
+       .parse = cmd_parse_string,
+       .complete_start = cmd_complete_string_start,
+       .complete_iterate = cmd_complete_string_iterate,
+       .complete_end = NULL,
+       .help = cmd_help_string,
+};
diff --git a/lib/cmd/ucg_cmd_rdline.c b/lib/cmd/ucg_cmd_rdline.c
new file mode 100644 (file)
index 0000000..25e65f0
--- /dev/null
@@ -0,0 +1,934 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_CMD_NO_PAGER
+#define _GNU_SOURCE /* for vasprintf */
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <alloca.h>
+
+#include <ucg_cirbuf.h>
+
+#include "ucg_cmd_rdline.h"
+#include "ucg_cmd_parse.h"
+
+#ifndef UCG_CMD_NO_RDLINE_HISTORY
+static void rdline_remove_old_history_item(struct ucg_rdline *rdl);
+static void rdline_remove_first_history_item(struct ucg_rdline *rdl);
+static unsigned int rdline_get_history_size(struct ucg_rdline *rdl);
+#endif /* !UCG_CMD_NO_RDLINE_HISTORY */
+
+#ifndef UCG_CMD_NO_PAGER
+static int rdline_pager_next_page(struct ucg_rdline *rdl);
+static void rdline_pager_reset(struct ucg_rdline *rdl);
+#endif /* !UCG_CMD_NO_PAGER */
+
+
+/* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
+ * own. */
+static int
+isblank2(char c)
+{
+       if (c == ' ' ||
+               c == '\t' )
+               return 1;
+       return 0;
+}
+
+void
+ucg_rdline_init(struct ucg_rdline *rdl,
+       FILE *f_in, FILE *f_out,
+       ucg_rdline_validate_t *validate,
+       ucg_rdline_complete_t *complete,
+       ucg_rdline_help_t *help)
+{
+       memset(rdl, 0, sizeof(*rdl));
+       rdl->f_in = f_in;
+       rdl->f_out = f_out;
+       rdl->validate = validate;
+       rdl->complete = complete;
+       rdl->help = help;
+       rdl->status = UCG_RDLINE_STOPPED;
+
+       /* Disable buffering */
+       setbuf(f_in, NULL);
+       setbuf(f_out, NULL);
+
+#ifndef UCG_CMD_NO_RDLINE_HISTORY
+       ucg_cirbuf_init(&rdl->history, rdl->history_buf, 0,
+               UCG_RDLINE_HISTORY_BUF_SIZE);
+#endif /* !UCG_CMD_NO_RDLINE_HISTORY */
+}
+
+void
+ucg_rdline_newline(struct ucg_rdline *rdl, const char *prompt)
+{
+       ucg_vt100_init(&rdl->vt100);
+       ucg_cirbuf_init(&rdl->left, rdl->left_buf, 0, UCG_RDLINE_BUF_SIZE);
+       ucg_cirbuf_init(&rdl->right, rdl->right_buf, 0, UCG_RDLINE_BUF_SIZE);
+
+       /* if pointer is the same or NULL, don't copy it */
+       if (prompt != NULL && prompt != rdl->prompt)
+               snprintf(rdl->prompt, sizeof(rdl->prompt), "%s", prompt);
+
+       ucg_rdline_printf(rdl, "%s", rdl->prompt);
+       rdl->status = UCG_RDLINE_RUNNING;
+
+#ifndef UCG_CMD_NO_RDLINE_HISTORY
+       rdl->history_cur_line = -1;
+#endif /* !UCG_CMD_NO_RDLINE_HISTORY */
+}
+
+void
+ucg_rdline_stop(struct ucg_rdline *rdl)
+{
+       rdl->status = UCG_RDLINE_STOPPED;
+}
+
+void
+ucg_rdline_quit(struct ucg_rdline *rdl)
+{
+       rdl->status = UCG_RDLINE_EXITED;
+}
+
+void
+ucg_rdline_restart(struct ucg_rdline *rdl)
+{
+       rdl->status = UCG_RDLINE_RUNNING;
+}
+
+const char *
+ucg_rdline_get_buffer(struct ucg_rdline *rdl)
+{
+       unsigned int len_l, len_r;
+       ucg_cirbuf_align_left(&rdl->left);
+       ucg_cirbuf_align_left(&rdl->right);
+
+       len_l = ucg_cirbuf_get_len(&rdl->left);
+       len_r = ucg_cirbuf_get_len(&rdl->right);
+       memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r);
+
+       rdl->left_buf[len_l + len_r] = '\0';
+       return rdl->left_buf;
+}
+
+static void
+display_right_buffer(struct ucg_rdline *rdl, int force)
+{
+       unsigned int i;
+       char tmp;
+
+       if (!force && ucg_cirbuf_is_empty(&rdl->right))
+               return;
+
+       ucg_rdline_printf(rdl, ucg_vt100_clear_right);
+       UCG_CIRBUF_FOREACH(&rdl->right, i, tmp) {
+               ucg_rdline_printf(rdl, "%c", tmp);
+       }
+       if (!ucg_cirbuf_is_empty(&rdl->right))
+               ucg_rdline_printf(rdl, ucg_vt100_multi_left,
+                       ucg_cirbuf_get_len(&rdl->right));
+}
+
+void
+ucg_rdline_redisplay(struct ucg_rdline *rdl)
+{
+       unsigned int i;
+       char tmp;
+
+       ucg_rdline_printf(rdl, ucg_vt100_home);
+       ucg_rdline_printf(rdl, "%s", rdl->prompt);
+       UCG_CIRBUF_FOREACH(&rdl->left, i, tmp) {
+               ucg_rdline_printf(rdl, "%c", tmp);
+       }
+       display_right_buffer(rdl, 1);
+}
+
+static int
+rdline_parse_char(struct ucg_rdline *rdl, char c)
+{
+       unsigned int i;
+       int cmd;
+       char tmp;
+#ifndef UCG_CMD_NO_RDLINE_HISTORY
+       const char *history;
+#endif
+
+       cmd = ucg_vt100_parser(&rdl->vt100, c);
+       if (cmd == UCG_VT100_NOT_COMPLETE)
+               return UCG_RDLINE_RES_SUCCESS;
+
+#ifndef UCG_CMD_NO_PAGER
+       /* display asynchrounous printf if any */
+       if (rdl->pager_buf != NULL) {
+
+               /* user ask to exit pager, or last page is displayed*/
+               if ((cmd == UCG_VT100_STD_CHAR && c == 'q') ||
+                       rdline_pager_next_page(rdl) == 0) {
+                       int ret;
+
+                       ret = rdl->pager_ret;
+                       rdline_pager_reset(rdl);
+                       if (rdl->pager_cb != NULL) {
+                               rdl->pager_cb(rdl, rdl->pager_arg);
+                               rdl->pager_cb = NULL;
+                       }
+                       /* maybe the pager was reloaded in the
+                        * callback */
+                       if (rdl->pager_buf != NULL)
+                               return UCG_RDLINE_RES_SUCCESS;
+
+                       /* else, redisplay prompt and return the saved status */
+                       ucg_rdline_redisplay(rdl);
+                       return ret;
+               }
+
+               /* Some pages remain, lines were displayed in
+                * rdline_pager_next_page() */
+               return UCG_RDLINE_RES_SUCCESS;
+       }
+#endif
+
+       /* process control chars */
+       if (cmd != UCG_VT100_STD_CHAR) {
+               switch (cmd) {
+               case UCG_CMD_KEY_CTRL_B:
+               case UCG_CMD_KEY_LEFT_ARR:
+                       if (ucg_cirbuf_is_empty(&rdl->left))
+                               break;
+                       tmp = ucg_cirbuf_get_tail(&rdl->left);
+                       ucg_cirbuf_del_tail(&rdl->left);
+                       ucg_cirbuf_add_head(&rdl->right, tmp);
+                       ucg_rdline_printf(rdl, ucg_vt100_left_arr);
+                       break;
+
+               case UCG_CMD_KEY_CTRL_F:
+               case UCG_CMD_KEY_RIGHT_ARR:
+                       if (ucg_cirbuf_is_empty(&rdl->right))
+                               break;
+                       tmp = ucg_cirbuf_get_head(&rdl->right);
+                       ucg_cirbuf_del_head(&rdl->right);
+                       ucg_cirbuf_add_tail(&rdl->left, tmp);
+                       ucg_rdline_printf(rdl, ucg_vt100_right_arr);
+                       break;
+
+               case UCG_CMD_KEY_WLEFT:
+                       while (! ucg_cirbuf_is_empty(&rdl->left) &&
+                               (tmp = ucg_cirbuf_get_tail(&rdl->left)) &&
+                               isblank2(tmp)) {
+                               ucg_rdline_printf(rdl, ucg_vt100_left_arr);
+                               ucg_cirbuf_del_tail(&rdl->left);
+                               ucg_cirbuf_add_head(&rdl->right, tmp);
+                       }
+                       while (! ucg_cirbuf_is_empty(&rdl->left) &&
+                               (tmp = ucg_cirbuf_get_tail(&rdl->left)) &&
+                               !isblank2(tmp)) {
+                               ucg_rdline_printf(rdl, ucg_vt100_left_arr);
+                               ucg_cirbuf_del_tail(&rdl->left);
+                               ucg_cirbuf_add_head(&rdl->right, tmp);
+                       }
+                       break;
+
+               case UCG_CMD_KEY_WRIGHT:
+                       while (! ucg_cirbuf_is_empty(&rdl->right) &&
+                               (tmp = ucg_cirbuf_get_head(&rdl->right)) &&
+                               isblank2(tmp)) {
+                               ucg_rdline_printf(rdl, ucg_vt100_right_arr);
+                               ucg_cirbuf_del_head(&rdl->right);
+                               ucg_cirbuf_add_tail(&rdl->left, tmp);
+                       }
+                       while (! ucg_cirbuf_is_empty(&rdl->right) &&
+                               (tmp = ucg_cirbuf_get_head(&rdl->right)) &&
+                               !isblank2(tmp)) {
+                               ucg_rdline_printf(rdl, ucg_vt100_right_arr);
+                               ucg_cirbuf_del_head(&rdl->right);
+                               ucg_cirbuf_add_tail(&rdl->left, tmp);
+                       }
+                       break;
+
+               case UCG_CMD_KEY_BKSPACE:
+                       if(!ucg_cirbuf_del_tail_safe(&rdl->left)) {
+                               ucg_rdline_printf(rdl, ucg_vt100_bs);
+                               display_right_buffer(rdl, 1);
+                       }
+                       break;
+
+               case UCG_CMD_KEY_META_BKSPACE:
+               case UCG_CMD_KEY_CTRL_W:
+                       while (! ucg_cirbuf_is_empty(&rdl->left) &&
+                               isblank2(ucg_cirbuf_get_tail(&rdl->left))) {
+                               ucg_rdline_printf(rdl, ucg_vt100_bs);
+                               ucg_cirbuf_del_tail(&rdl->left);
+                       }
+                       while (! ucg_cirbuf_is_empty(&rdl->left) &&
+                               !isblank2(ucg_cirbuf_get_tail(&rdl->left))) {
+                               ucg_rdline_printf(rdl, ucg_vt100_bs);
+                               ucg_cirbuf_del_tail(&rdl->left);
+                       }
+                       display_right_buffer(rdl, 1);
+                       break;
+
+               case UCG_CMD_KEY_META_D:
+                       while (! ucg_cirbuf_is_empty(&rdl->right) &&
+                               isblank2(ucg_cirbuf_get_head(&rdl->right)))
+                               ucg_cirbuf_del_head(&rdl->right);
+                       while (! ucg_cirbuf_is_empty(&rdl->right) &&
+                               !isblank2(ucg_cirbuf_get_head(&rdl->right)))
+                               ucg_cirbuf_del_head(&rdl->right);
+                       display_right_buffer(rdl, 1);
+                       break;
+
+               case UCG_CMD_KEY_SUPPR:
+               case UCG_CMD_KEY_CTRL_D:
+                       if (cmd == UCG_CMD_KEY_CTRL_D &&
+                               ucg_cirbuf_is_empty(&rdl->left) &&
+                               ucg_cirbuf_is_empty(&rdl->right)) {
+                               return UCG_RDLINE_RES_EOF;
+                       }
+                       if (!ucg_cirbuf_del_head_safe(&rdl->right)) {
+                               display_right_buffer(rdl, 1);
+                       }
+                       break;
+
+               case UCG_CMD_KEY_CTRL_A:
+                       if (ucg_cirbuf_is_empty(&rdl->left))
+                               break;
+                       ucg_rdline_printf(rdl, ucg_vt100_multi_left,
+                               ucg_cirbuf_get_len(&rdl->left));
+                       while (! ucg_cirbuf_is_empty(&rdl->left)) {
+                               tmp = ucg_cirbuf_get_tail(&rdl->left);
+                               ucg_cirbuf_del_tail(&rdl->left);
+                               ucg_cirbuf_add_head(&rdl->right, tmp);
+                       }
+                       break;
+
+               case UCG_CMD_KEY_CTRL_E:
+                       if (ucg_cirbuf_is_empty(&rdl->right))
+                               break;
+                       ucg_rdline_printf(rdl, ucg_vt100_multi_right,
+                               ucg_cirbuf_get_len(&rdl->right));
+                       while (! ucg_cirbuf_is_empty(&rdl->right)) {
+                               tmp = ucg_cirbuf_get_head(&rdl->right);
+                               ucg_cirbuf_del_head(&rdl->right);
+                               ucg_cirbuf_add_tail(&rdl->left, tmp);
+                       }
+                       break;
+
+#ifndef UCG_CMD_NO_RDLINE_KILL_BUF
+               case UCG_CMD_KEY_CTRL_K:
+                       ucg_cirbuf_get_buf_head(&rdl->right, rdl->kill_buf,
+                               UCG_RDLINE_BUF_SIZE);
+                       rdl->kill_size = ucg_cirbuf_get_len(&rdl->right);
+                       ucg_cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
+                       ucg_rdline_printf(rdl, ucg_vt100_clear_right);
+                       break;
+
+               case UCG_CMD_KEY_CTRL_Y:
+                       i=0;
+                       while (ucg_cirbuf_get_len(&rdl->right) +
+                               ucg_cirbuf_get_len(&rdl->left) <
+                               UCG_RDLINE_BUF_SIZE &&
+                               i < rdl->kill_size) {
+                               ucg_cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
+                               ucg_rdline_printf(rdl, "%c", rdl->kill_buf[i]);
+                               i++;
+                       }
+                       display_right_buffer(rdl, 0);
+                       break;
+#endif /* !UCG_CMD_NO_RDLINE_KILL_BUF */
+
+               case UCG_CMD_KEY_CTRL_C:
+                       ucg_rdline_printf(rdl, "\r\n");
+                       ucg_rdline_newline(rdl, rdl->prompt);
+                       break;
+
+               case UCG_CMD_KEY_CTRL_L:
+                       ucg_rdline_redisplay(rdl);
+                       break;
+
+               case UCG_CMD_KEY_HELP: {
+                       if (rdl->help == NULL)
+                               break;
+
+                       ucg_cirbuf_align_left(&rdl->left);
+                       rdl->left_buf[ucg_cirbuf_get_len(&rdl->left)] = '\0';
+                       ucg_rdline_printf(rdl, "\r\n");
+                       rdl->help(rdl, rdl->left_buf);
+#ifndef UCG_CMD_NO_PAGER
+                       if (rdl->pager_buf != NULL)
+                               return UCG_RDLINE_RES_SUCCESS;
+                       else
+                               rdline_pager_reset(rdl);
+#endif
+                       ucg_rdline_redisplay(rdl);
+                       break;
+               }
+
+               case UCG_CMD_KEY_TAB: {
+                       char tmp_buf[UCG_CMD_MAX_TOKEN_SIZE];
+                       int ret;
+                       unsigned int tmp_size;
+
+                       if (rdl->complete == NULL)
+                               break;
+
+                       ucg_cirbuf_align_left(&rdl->left);
+                       rdl->left_buf[ucg_cirbuf_get_len(&rdl->left)] = '\0';
+
+                       /* try to complete the  complete() */
+                       ret = rdl->complete(rdl, rdl->left_buf,
+                               tmp_buf, sizeof(tmp_buf));
+
+                       /* no completion or error */
+                       if (ret < 0) {
+                               ucg_cirbuf_align_left(&rdl->left);
+                               rdl->left_buf[ucg_cirbuf_get_len(&rdl->left)] = '\0';
+                               ucg_rdline_printf(rdl, "\r\n");
+                               rdl->help(rdl, rdl->left_buf);
+#ifndef UCG_CMD_NO_PAGER
+                               if (rdl->pager_buf != NULL)
+                                       return UCG_RDLINE_RES_SUCCESS;
+                               else
+                                       rdline_pager_reset(rdl);
+#endif
+                               ucg_rdline_redisplay(rdl);
+                               break;
+                       }
+
+                       tmp_size = strlen(tmp_buf);
+                       /* add chars */
+                       i = 0;
+                       while(ucg_cirbuf_get_len(&rdl->right) +
+                               ucg_cirbuf_get_len(&rdl->left) <
+                               UCG_RDLINE_BUF_SIZE &&
+                               i < tmp_size) {
+                               ucg_cirbuf_add_tail(&rdl->left, tmp_buf[i]);
+                               ucg_rdline_printf(rdl, "%c", tmp_buf[i]);
+                               i++;
+                       }
+                       display_right_buffer(rdl, 1);
+                       break;
+               }
+
+               case UCG_CMD_KEY_RETURN:
+               case UCG_CMD_KEY_RETURN2: {
+                       char tmp;
+                       while (!ucg_cirbuf_is_empty(&rdl->right) &&
+                               (tmp = ucg_cirbuf_get_head(&rdl->right))) {
+                               ucg_cirbuf_del_head(&rdl->right);
+                               ucg_cirbuf_add_tail(&rdl->left, tmp);
+                       }
+                       ucg_cirbuf_align_left(&rdl->left);
+                       rdl->left_buf[ucg_cirbuf_get_len(&rdl->left)] = '\0';
+                       ucg_rdline_printf(rdl, "\r\n");
+#ifndef UCG_CMD_NO_RDLINE_HISTORY
+                       if (rdl->history_cur_line != -1)
+                               rdline_remove_first_history_item(rdl);
+#endif
+
+                       if (rdl->validate)
+                               rdl->validate(rdl, rdl->left_buf);
+#ifndef UCG_CMD_NO_PAGER
+                       /* user may have stopped rdline */
+                       if (rdl->status == UCG_RDLINE_EXITED) {
+                               rdline_pager_reset(rdl);
+                               return UCG_RDLINE_RES_EXITED;
+                       }
+                       /* there is something in pager buffer, save
+                        * return value that will be return once
+                        * paging is finished */
+                       if (rdl->pager_buf != NULL) {
+                               rdl->pager_ret = UCG_RDLINE_RES_VALIDATED;
+                               return UCG_RDLINE_RES_SUCCESS;
+                       }
+
+                       rdline_pager_reset(rdl);
+#else
+                       if (rdl->status == UCG_RDLINE_EXITED)
+                               return UCG_RDLINE_RES_EXITED;
+#endif
+                       return UCG_RDLINE_RES_VALIDATED;
+               }
+#ifndef UCG_CMD_NO_RDLINE_HISTORY
+               case UCG_CMD_KEY_UP_ARR:
+               case UCG_CMD_KEY_CTRL_P:
+                       if (rdl->history_cur_line == 0) {
+                               rdline_remove_first_history_item(rdl);
+                       }
+                       if (rdl->history_cur_line <= 0) {
+                               ucg_rdline_add_history(rdl,
+                                       ucg_rdline_get_buffer(rdl));
+                               rdl->history_cur_line = 0;
+                       }
+
+                       history = ucg_rdline_get_history_item(rdl,
+                               rdl->history_cur_line + 1);
+                       if (history == NULL)
+                               break;
+
+                       rdl->history_cur_line++;
+                       ucg_vt100_init(&rdl->vt100);
+                       ucg_cirbuf_init(&rdl->left, rdl->left_buf, 0,
+                               UCG_RDLINE_BUF_SIZE);
+                       ucg_cirbuf_init(&rdl->right, rdl->right_buf, 0,
+                               UCG_RDLINE_BUF_SIZE);
+                       ucg_cirbuf_add_buf_tail(&rdl->left, history,
+                               strlen(history));
+                       ucg_rdline_redisplay(rdl);
+                       break;
+
+               case UCG_CMD_KEY_DOWN_ARR:
+               case UCG_CMD_KEY_CTRL_N:
+                       if (rdl->history_cur_line - 1 < 0)
+                               break;
+
+                       rdl->history_cur_line--;
+                       history = ucg_rdline_get_history_item(rdl,
+                               rdl->history_cur_line);
+                       if (history == NULL)
+                               break;
+
+                       ucg_vt100_init(&rdl->vt100);
+                       ucg_cirbuf_init(&rdl->left, rdl->left_buf, 0,
+                               UCG_RDLINE_BUF_SIZE);
+                       ucg_cirbuf_init(&rdl->right, rdl->right_buf, 0,
+                               UCG_RDLINE_BUF_SIZE);
+                       ucg_cirbuf_add_buf_tail(&rdl->left, history,
+                               strlen(history));
+                       ucg_rdline_redisplay(rdl);
+
+                       break;
+#endif /* !UCG_CMD_NO_RDLINE_HISTORY */
+
+               default:
+                       break;
+               }
+
+               return UCG_RDLINE_RES_SUCCESS;
+       }
+
+       if (!isprint((int)c))
+               return UCG_RDLINE_RES_SUCCESS;
+
+       /* standard chars */
+       if (ucg_cirbuf_get_len(&rdl->left) +
+               ucg_cirbuf_get_len(&rdl->right) >= UCG_RDLINE_BUF_SIZE)
+               return UCG_RDLINE_RES_SUCCESS;
+
+       if (ucg_cirbuf_add_tail_safe(&rdl->left, c))
+               return UCG_RDLINE_RES_SUCCESS;
+
+       ucg_rdline_printf(rdl, "%c", c);
+       display_right_buffer(rdl, 0);
+
+       return UCG_RDLINE_RES_SUCCESS;
+}
+
+int
+ucg_rdline_char_in(struct ucg_rdline *rdl, char c)
+{
+       int ret, same = 0;
+       const char *history, *buffer;
+
+       if (rdl->status == UCG_RDLINE_EXITED)
+               return UCG_RDLINE_RES_EXITED;
+       if (rdl->status != UCG_RDLINE_RUNNING)
+               return UCG_RDLINE_RES_NOT_RUNNING;
+
+       ret = rdline_parse_char(rdl, c);
+
+       /* add line to history */
+       if (ret == UCG_RDLINE_RES_VALIDATED) {
+               buffer = ucg_rdline_get_buffer(rdl);
+               history = ucg_rdline_get_history_item(rdl, 0);
+               if (history)
+                       same = !strcmp(buffer, history);
+
+               if (strlen(buffer) >= 1 && same == 0)
+                       ucg_rdline_add_history(rdl, buffer);
+       }
+
+       return ret;
+}
+
+int
+ucg_rdline(struct ucg_rdline *rdl, const char *prompt, unsigned flags)
+{
+       char c;
+       int ret = UCG_RDLINE_RES_NOT_RUNNING;
+
+       ucg_rdline_newline(rdl, prompt);
+       while (1) {
+               if (fread(&c, 1, 1, rdl->f_in) == 0) {
+                       if (flags & UCG_RDLINE_F_IGNORE_EOF) {
+                               clearerr(rdl->f_in);
+                               continue;
+                       }
+                       break;
+               }
+               ret = ucg_rdline_char_in(rdl, c);
+               if (ret != UCG_RDLINE_RES_SUCCESS)
+                       break;
+       }
+
+       return ret;
+}
+
+/* HISTORY */
+
+#ifndef UCG_CMD_NO_RDLINE_HISTORY
+static void
+rdline_remove_old_history_item(struct ucg_rdline *rdl)
+{
+       char tmp;
+
+       while (! ucg_cirbuf_is_empty(&rdl->history) ) {
+               tmp = ucg_cirbuf_get_head(&rdl->history);
+               ucg_cirbuf_del_head(&rdl->history);
+               if (!tmp)
+                       break;
+       }
+}
+
+static void
+rdline_remove_first_history_item(struct ucg_rdline *rdl)
+{
+       char tmp;
+
+       if ( ucg_cirbuf_is_empty(&rdl->history) ) {
+               return;
+       }
+       else {
+               ucg_cirbuf_del_tail(&rdl->history);
+       }
+
+       while (! ucg_cirbuf_is_empty(&rdl->history) ) {
+               tmp = ucg_cirbuf_get_tail(&rdl->history);
+               if (!tmp)
+                       break;
+               ucg_cirbuf_del_tail(&rdl->history);
+       }
+}
+
+static unsigned int
+rdline_get_history_size(struct ucg_rdline *rdl)
+{
+       unsigned int i, tmp, ret=0;
+
+       UCG_CIRBUF_FOREACH(&rdl->history, i, tmp) {
+               if (tmp == 0)
+                       ret ++;
+       }
+
+       return ret;
+}
+
+const char *
+ucg_rdline_get_history_item(struct ucg_rdline *rdl, unsigned int idx)
+{
+       unsigned int len, i, tmp;
+
+       len = rdline_get_history_size(rdl);
+       if (idx >= len)
+               return NULL;
+
+       ucg_cirbuf_align_left(&rdl->history);
+
+       UCG_CIRBUF_FOREACH(&rdl->history, i, tmp) {
+               if (idx == len - 1) {
+                       return rdl->history_buf + i;
+               }
+               if (tmp == 0)
+                       len --;
+       }
+
+       return NULL;
+}
+
+int
+ucg_rdline_add_history(struct ucg_rdline *rdl, const char *buf)
+{
+       unsigned int len;
+
+       len = strlen(buf);
+       if (len >= UCG_RDLINE_HISTORY_BUF_SIZE)
+               return -1;
+
+       while (len >= ucg_cirbuf_get_freelen(&rdl->history)) {
+               rdline_remove_old_history_item(rdl);
+       }
+
+       ucg_cirbuf_add_buf_tail(&rdl->history, buf, len);
+       ucg_cirbuf_add_tail(&rdl->history, 0);
+
+       return 0;
+}
+
+void
+ucg_rdline_clear_history(struct ucg_rdline *rdl)
+{
+       ucg_cirbuf_init(&rdl->history, rdl->history_buf, 0,
+               UCG_RDLINE_HISTORY_BUF_SIZE);
+}
+
+#else /* !UCG_CMD_NO_RDLINE_HISTORY */
+
+int ucg_rdline_add_history(struct ucg_rdline *rdl, const char *buf)
+{
+       return -1;
+}
+
+void ucg_rdline_clear_history(struct ucg_rdline *rdl)
+{
+       return;
+}
+
+char *ucg_rdline_get_history_item(struct ucg_rdline *rdl, unsigned int i)
+{
+       return NULL;
+}
+
+
+#endif /* !UCG_CMD_NO_RDLINE_HISTORY */
+
+
+ssize_t
+ucg_rdline_write(struct ucg_rdline *rdl, void *buf, size_t count)
+{
+       return fwrite(buf, 1, count, rdl->f_out);
+}
+
+int
+ucg_rdline_vprintf(struct ucg_rdline *rdl, const char *fmt, va_list ap)
+{
+       if (rdl->f_out == NULL)
+               return -1;
+
+       return vfprintf(rdl->f_out, fmt, ap);
+}
+
+int
+ucg_rdline_printf(struct ucg_rdline *rdl, const char *fmt, ...)
+{
+       va_list ap;
+       int ret;
+
+       va_start(ap, fmt);
+       ret = ucg_rdline_vprintf(rdl, fmt, ap);
+       va_end(ap);
+
+       return ret;
+}
+
+#ifndef UCG_CMD_NO_PAGER
+/* reset pager state */
+void
+rdline_pager_reset(struct ucg_rdline *rdl)
+{
+       if (rdl->pager_buf) {
+               free(rdl->pager_buf);
+               rdl->pager_buf = NULL;
+       }
+       rdl->pager_lines = 0;
+       rdl->pager_len = 0;
+       rdl->pager_off = 0;
+       rdl->pager_cb = NULL;
+       rdl->pager_arg = NULL;
+       rdl->pager_ret = UCG_RDLINE_RES_SUCCESS;
+}
+
+/* Return the offset of the i-th occurence of char c in string s. If
+ * there is less than i occurences, return -1 and fill i with the
+ * count. */
+static int
+strnchr(const char *s, char c, int *i)
+{
+       int n = 0;
+       const char *orig = s;
+
+       while (*s) {
+               if (*s == c)
+                       n++;
+               if (*i == n)
+                       return s - orig;
+               s++;
+       }
+       *i = n;
+       return -1;
+}
+
+/* display a page of data from pager, return 0 if all is displayed */
+static int
+rdline_pager_next_page(struct ucg_rdline *rdl)
+{
+       int lines = UCG_RDLINE_MAX_LINES;
+       int displen;
+       char *s;
+
+       s = rdl->pager_buf;
+       if (s == NULL)
+               return 0;
+
+       ucg_rdline_printf(rdl, ucg_vt100_home);
+       ucg_rdline_printf(rdl, ucg_vt100_clear_right);
+
+       s += rdl->pager_off;
+
+       /* we know that s is 0-terminated */
+       displen = strnchr(s, '\n', &lines);
+       rdl->pager_lines = lines;
+
+       /* we can display all the data */
+       if (displen == -1) {
+               fwrite(s, 1, rdl->pager_len, rdl->f_out);
+               free(rdl->pager_buf);
+               rdl->pager_buf = NULL;
+               return 0;
+       }
+
+       displen = displen + 1; /* include \n */
+       fwrite(s, 1, displen, rdl->f_out);
+       rdl->pager_off += displen;
+       rdl->pager_len -= displen;
+
+       ucg_rdline_printf(rdl, "--- press a key to continue ---");
+       return -1;
+}
+
+/* push data in pager */
+ssize_t
+ucg_rdline_pager_write(struct ucg_rdline *rdl, void *buf, size_t len)
+{
+       char *s = buf;
+
+       /* display as many lines as we can */
+       if (rdl->pager_lines < UCG_RDLINE_MAX_LINES) {
+               int lines = UCG_RDLINE_MAX_LINES - rdl->pager_lines;
+               int displen;
+
+               /* we know that s is 0-terminated */
+               displen = strnchr(s, '\n', &lines);
+               rdl->pager_lines += lines;
+
+               /* we can display all the data */
+               if (displen == -1) {
+                       fwrite(s, 1, len, rdl->f_out);
+                       return 0;
+               }
+               displen = displen + 1; /* include \n */
+               fwrite(s, 1, displen, rdl->f_out);
+               s += displen;
+               len -= displen;
+       }
+
+       if (rdl->pager_buf == NULL) {
+               ucg_rdline_printf(rdl, "--- press a key to continue ---");
+       }
+       rdl->pager_buf = realloc(rdl->pager_buf, rdl->pager_len + len);
+       if (rdl->pager_buf == NULL) {
+               rdline_pager_reset(rdl);
+               return -1;
+       }
+
+       memcpy(rdl->pager_buf + rdl->pager_len, s, len);
+       rdl->pager_len += len;
+       return 0;
+}
+
+/* Print data asynchronously (using pager if needed) */
+int
+ucg_rdline_pager_printf(struct ucg_rdline *rdl, const char *fmt, ...)
+{
+       int n;
+       char *buf = NULL;
+       va_list ap;
+
+       if (rdl->f_out == NULL)
+               return -1;
+
+       va_start(ap, fmt);
+       n = vasprintf(&buf, fmt, ap);
+       va_end(ap);
+
+       if (n > 0)
+               ucg_rdline_pager_write(rdl, buf, n);
+       free(buf);
+       return n;
+}
+
+int ucg_rdline_pager_set_cb(struct ucg_rdline *rdl,
+       ucg_rdline_pager_cb_t *cb, void *arg)
+{
+       if (rdl->pager_buf == NULL)
+               return -1;
+
+       rdl->pager_cb = cb;
+       rdl->pager_arg = arg;
+       return 0;
+}
+#endif /* !UCG_CMD_NO_PAGER */
diff --git a/lib/cmd/ucg_cmd_socket.c b/lib/cmd/ucg_cmd_socket.c
new file mode 100644 (file)
index 0000000..84bc226
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#ifdef UCG_CMD_HAVE_TERMIOS
+#include <termios.h>
+#endif
+
+#ifdef UCG_CMD_HAVE_SOCKET
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
+#include "ucg_cmd_parse.h"
+#include "ucg_cmd_rdline.h"
+#include "ucg_cmd_socket.h"
+#include "ucg_cmd.h"
+
+#ifdef UCG_CMD_HAVE_SOCKET
+int
+ucg_cmd_tcpv4_listen(in_addr_t addr, uint16_t port)
+{
+       int s;
+       struct sockaddr_in sin_ci;
+       int optval = 1;
+
+       s = socket(PF_INET, SOCK_STREAM, 0);
+       if (s < 0)
+               return s;
+
+       if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+                       &optval, sizeof(optval)) == -1)
+               goto end;
+
+       memset(&sin_ci, 0, sizeof(sin_ci));
+       sin_ci.sin_family = AF_INET;
+       sin_ci.sin_addr.s_addr = addr;
+       sin_ci.sin_port = htons(port);
+#ifndef __linux__
+       sin_ci.sin_len = sizeof(sin_ci);
+#endif
+       if (bind(s, (struct sockaddr *)&sin_ci, sizeof(sin_ci)) < 0)
+               goto end;
+
+       if (listen(s, 1) < 0)
+               goto end;
+
+       return s;
+ end:
+       close(s);
+       return -1;
+}
+
+int
+ucg_cmd_tcpv6_listen(const struct in6_addr *addr6, uint16_t port)
+{
+       int s;
+       struct sockaddr_in6 sin6_ci;
+
+       s = socket(PF_INET6, SOCK_STREAM, 0);
+       if (s < 0)
+               return s;
+
+       bzero(&sin6_ci, sizeof(sin6_ci));
+       sin6_ci.sin6_family = AF_INET6;
+#ifndef __linux__
+       sin6_ci.sin6_len = sizeof(sin6_ci);
+#endif
+       memcpy(&sin6_ci.sin6_addr, addr6, sizeof(sin6_ci.sin6_addr));
+       sin6_ci.sin6_port = htons(port);
+       if (bind(s, (struct sockaddr *) &sin6_ci, sizeof(sin6_ci)) < 0)
+               goto end;
+
+       if (listen(s, 1) < 0)
+               goto end;
+
+       return s;
+ end:
+       close(s);
+       return -1;
+}
+
+int
+ucg_cmd_unix_listen(const char *filename)
+{
+       int s;
+       struct sockaddr_un servAddr;
+
+       s = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (s < 0)
+               return s;
+
+       bzero(&servAddr, sizeof(servAddr));
+       servAddr.sun_family = AF_UNIX;
+       memcpy(servAddr.sun_path, filename , strlen(filename));
+
+       unlink(filename);
+       if(bind(s, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0)
+               goto end;
+
+       if (listen(s, 1) < 0)
+               goto end;
+
+       return s;
+ end:
+       close(s);
+       return -1;
+}
+
+struct ucg_cmd *
+ucg_cmd_accept(ucg_cmd_ctx_t *ctx, const char *prompt, int s)
+{
+       FILE *f;
+       int s2;
+       struct sockaddr sin;
+       socklen_t sinlen;
+
+       sinlen = sizeof(struct sockaddr);
+
+       if ((s2 = accept(s, &sin, &sinlen)) < 0)
+               return NULL;
+
+       f = fdopen(s2, "r+");
+       if (f == NULL) {
+               close(s2);
+               return NULL;
+       }
+       return (ucg_cmd_new(ctx, prompt, f, f));
+}
+#endif
diff --git a/lib/cmd/ucg_cmd_termios.c b/lib/cmd/ucg_cmd_termios.c
new file mode 100644 (file)
index 0000000..bb4b1ea
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#ifdef UCG_CMD_HAVE_TERMIOS
+#include <termios.h>
+#endif
+
+#include "ucg_cmd.h"
+#include "ucg_cmd_termios.h"
+
+int ucg_cmd_termios_raw(struct ucg_cmd *cl)
+{
+       int ret = 0;
+
+#ifdef UCG_CMD_HAVE_TERMIOS
+       struct termios oldterm, term;
+
+       ret = tcgetattr(0, &oldterm);
+       if (ret < 0)
+               return ret;
+
+       memcpy(&term, &oldterm, sizeof(term));
+       term.c_lflag &= ~(ICANON | ECHO | ISIG);
+       ret = tcsetattr(0, TCSANOW, &term);
+       if (ret < 0)
+               return ret;
+
+       memcpy(&cl->oldterm, &oldterm, sizeof(term));
+#endif
+
+       (void)cl; /* silent compiler */
+
+       return ret;
+}
+
+int ucg_cmd_termios_restore(struct ucg_cmd *cl)
+{
+       int ret = 0;
+       (void)cl; /* silent compiler */
+
+#ifdef UCG_CMD_HAVE_TERMIOS
+       ret = tcsetattr(fileno(stdin), TCSANOW, &cl->oldterm);
+#endif
+       return ret;
+}
+
diff --git a/lib/cmd/ucg_cmd_vt100.c b/lib/cmd/ucg_cmd_vt100.c
new file mode 100644 (file)
index 0000000..66d8184
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) <2010>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "ucg_cmd_vt100.h"
+
+const char *ucg_cmd_vt100_commands[] = {
+       ucg_vt100_up_arr,
+       ucg_vt100_down_arr,
+       ucg_vt100_right_arr,
+       ucg_vt100_left_arr,
+       "\177",
+       "\n",
+       "\001",
+       "\005",
+       "\013",
+       "\031",
+       "\003",
+       "\006",
+       "\002",
+       ucg_vt100_suppr,
+       ucg_vt100_tab,
+       "\004",
+       "\014",
+       "\r",
+       "\033\177",
+       ucg_vt100_word_left,
+       ucg_vt100_word_right,
+       "?",
+       "\027",
+       "\020",
+       "\016",
+       "\033\144",
+};
+
+void
+ucg_vt100_init(struct ucg_cmd_vt100 *vt)
+{
+       vt->state = UCG_CMD_VT100_INIT;
+}
+
+
+static int
+match_command(char *buf, unsigned int size)
+{
+       const char *cmd;
+       unsigned int i = 0;
+
+       for (i = 0; i < sizeof(ucg_cmd_vt100_commands) /
+                    sizeof(const char *); i++) {
+               cmd = *(ucg_cmd_vt100_commands + i);
+
+               if (size == strlen(cmd) &&
+                   !strncmp(buf, cmd, strlen(cmd))) {
+                       return i;
+               }
+       }
+
+       return -1;
+}
+
+int
+ucg_vt100_parser(struct ucg_cmd_vt100 *vt, char ch)
+{
+       unsigned int size;
+       uint8_t c = (uint8_t) ch;
+
+       if (vt->bufpos > UCG_CMD_VT100_BUF_SIZE) {
+               vt->state = UCG_CMD_VT100_INIT;
+               vt->bufpos = 0;
+       }
+
+       vt->buf[vt->bufpos++] = c;
+       size = vt->bufpos;
+
+       switch (vt->state) {
+       case UCG_CMD_VT100_INIT:
+               if (c == 033) {
+                       vt->state = UCG_CMD_VT100_ESCAPE;
+               }
+               else {
+                       vt->bufpos = 0;
+                       goto match_command;
+               }
+               break;
+
+       case UCG_CMD_VT100_ESCAPE:
+               if (c == 0133) {
+                       vt->state = UCG_CMD_VT100_ESCAPE_CSI;
+               }
+               else if (c >= 060 && c <= 0177) {
+                       vt->bufpos = 0;
+                       vt->state = UCG_CMD_VT100_INIT;
+                       goto match_command;
+               }
+               break;
+
+       case UCG_CMD_VT100_ESCAPE_CSI:
+               if (c >= 0100 && c <= 0176) {
+                       vt->bufpos = 0;
+                       vt->state = UCG_CMD_VT100_INIT;
+                       goto match_command;
+               }
+               break;
+
+       default:
+               vt->bufpos = 0;
+               break;
+       }
+
+       return -2;
+
+ match_command:
+       return match_command(vt->buf, size);
+}
diff --git a/lib/gloss/include/ucg_gloss_chardev.h b/lib/gloss/include/ucg_gloss_chardev.h
new file mode 100644 (file)
index 0000000..92e5f8d
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_GLOSS_DEVICE_H_
+#define UCG_GLOSS_DEVICE_H_
+
+#include <sys/queue.h>
+#include <string.h>
+
+/**
+ * Maximum number of devices stored in cache.
+ *
+ * Instead of only storing the devices in a list, the first ones are
+ * stored in a table indexed by the file descriptor. This avoids a
+ * linear browsing on lookup.
+ */
+#define GLOSS_DEVICE_CACHE_MAX 4
+
+/**
+ * Structure describing a device
+ *
+ * It can be registered using ucg_chardev_register(), and allows to
+ * redirect file operations to any channel (uart, spi, ...).
+ */
+struct ucg_chardev {
+       /* filled by ucg_chardev_register() */
+       TAILQ_ENTRY(ucg_chardev) next;
+       int fd;
+
+       /* filled by user before registration */
+       const char *name;
+       int (*open_r)(struct _reent *r, const char *path, int flags, int mode);
+       int (*close_r)(struct _reent *r, int fd);
+       _ssize_t (*read_r)(struct _reent *r, int fd, void *ptr, size_t len);
+       _ssize_t (*write_r)(struct _reent *r, int fd, const void *ptr, size_t len);
+};
+
+/**
+ * The list of devices.
+ */
+TAILQ_HEAD(ucg_chardev_list, ucg_chardev);
+struct ucg_chardev_list ucg_chardev_list;
+
+/**
+ * Table of first ucg_chardev pointers, initialized to NULL.
+ */
+struct ucg_chardev *ucg_chardev_cache[GLOSS_DEVICE_CACHE_MAX];
+
+/**
+ * Register a device
+ *
+ * Register device operations. The structure is appended to the device
+ * list, and its name and fd fields are modified. Therefore, a structure
+ * cannot be registered twice.
+ *
+ * The first three devices to be registered are stdin, stdout and stderr.
+ *
+ * @param dev
+ *   The filled device structure
+ * @return
+ *   0 on success, negative on error
+ */
+int ucg_chardev_register(struct ucg_chardev *dev);
+
+/**
+ * Return the device associated to the given file descriptor.
+ *
+ * @param fd
+ *   The file descriptor.
+ * @return
+ *   The device matching the file descriptor.
+ */
+static inline const struct ucg_chardev *
+ucg_chardev_lookup_from_fd(int fd)
+{
+       const struct ucg_chardev *dev;
+
+       if (fd >= 0 && fd < GLOSS_DEVICE_CACHE_MAX)
+               return ucg_chardev_cache[fd];
+
+       TAILQ_FOREACH(dev, &ucg_chardev_list, next) {
+               if (dev->fd == fd)
+                       return dev;
+       }
+       return NULL;
+}
+
+/**
+ * Return the device associated to the given name.
+ *
+ * @param name
+ *   The name of the file.
+ * @return
+ *   The device matching the file name.
+ */
+static inline const struct ucg_chardev *
+ucg_chardev_lookup_from_name(const char *name)
+{
+       const struct ucg_chardev *dev;
+
+       TAILQ_FOREACH(dev, &ucg_chardev_list, next) {
+               if (strcmp(name, dev->name) == 0)
+                       return dev;
+       }
+       return NULL;
+}
+
+#endif /* UCG_GLOSS_DEVICE_H_ */
diff --git a/lib/gloss/ucg_gloss_chardev.c b/lib/gloss/ucg_gloss_chardev.c
new file mode 100644 (file)
index 0000000..972c987
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/queue.h>
+#include <errno.h>
+
+#include <ucg_gloss_chardev.h>
+
+struct ucg_chardev_list ucg_chardev_list =
+       TAILQ_HEAD_INITIALIZER(ucg_chardev_list);
+
+static int max_fd = 0;
+
+/* register a device for file operations */
+int ucg_chardev_register(struct ucg_chardev *dev)
+{
+       if (dev->name == NULL)
+               return -EINVAL;
+
+       if (ucg_chardev_lookup_from_name(dev->name) != NULL)
+               return -EEXIST;
+
+       TAILQ_INSERT_TAIL(&ucg_chardev_list, dev, next);
+
+       dev->fd = max_fd ++;
+       if (dev->fd < GLOSS_DEVICE_CACHE_MAX)
+               ucg_chardev_cache[dev->fd] = dev;
+       return 0;
+}
diff --git a/lib/gloss/ucg_gloss_stubs.c b/lib/gloss/ucg_gloss_stubs.c
new file mode 100644 (file)
index 0000000..03d9ed6
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <reent.h>
+#include <errno.h>
+
+#include <ucg_gloss_chardev.h>
+
+/* provide a dummy environment */
+char *__env[1] = { 0 };
+char **environ = __env;
+
+/* provide a definition of errno if not already provided */
+int errno;
+
+/* defined by the linker, used by _sbrk */
+extern char _ebss;
+
+/* open a device, return its file descriptor */
+int
+_open_r(struct _reent *reent, const char *file, int flags, int mode)
+{
+       const struct ucg_chardev *dev;
+
+       dev = ucg_chardev_lookup_from_name(file);
+       if (dev == NULL) {
+               reent->_errno = ENODEV;
+               return -1;
+       }
+
+       if (dev->open_r)
+               dev->open_r(reent, file, flags, mode);
+
+       return dev->fd;
+}
+
+/* close a device */
+int
+_close_r(struct _reent *reent, int fd)
+{
+       const struct ucg_chardev *dev;
+
+       dev = ucg_chardev_lookup_from_fd(fd);
+       if (dev == NULL) {
+               reent->_errno = EBADF;
+               return -1;
+       }
+
+       /* call the close() handler if defined */
+       if (dev->close_r)
+               return dev->close_r(reent, fd);
+
+       return 0;
+}
+
+/* read a device. */
+_ssize_t
+_read_r(struct _reent *reent, int fd, void *ptr, size_t len)
+{
+       const struct ucg_chardev *dev;
+
+       dev = ucg_chardev_lookup_from_fd(fd);
+       if (dev == NULL) {
+               reent->_errno = EBADF;
+               return -1;
+       }
+
+       if (dev->read_r)
+               return dev->read_r(reent, fd, ptr, len);
+
+       reent->_errno = ENODEV;
+       return -1;
+}
+
+/* write to a device */
+_ssize_t
+_write_r(struct _reent *reent, int fd, const void *ptr, size_t len)
+{
+       const struct ucg_chardev *dev;
+
+       dev = ucg_chardev_lookup_from_fd(fd);
+       if (dev == NULL) {
+               reent->_errno = EBADF;
+               return -1;
+       }
+
+       if (dev->write_r)
+               return dev->write_r(reent, fd, ptr, len);
+
+       reent->_errno = ENODEV;
+       return -1;
+}
+
+/* exit: print error code and loop forever */
+void
+_exit(int rc)
+{
+       printf("exit %d\n", rc);
+       while (1);
+}
+
+/* set owner: not implemented */
+int
+_chown_r(struct _reent *reent,
+        __attribute__((unused)) const char *path,
+        __attribute__((unused)) uid_t owner,
+        __attribute__((unused)) gid_t group)
+{
+       reent->_errno = ENOSYS;
+       return -1;
+}
+
+/* execve: not implemented */
+int
+_execve_r(struct _reent *reent,
+         __attribute__((unused)) const char *name,
+         __attribute__((unused)) char *const *argv,
+         __attribute__((unused)) char *const *env)
+{
+       reent->_errno = ENOSYS;
+       return -1;
+}
+
+/* fork: not implemented */
+int
+_fork_r(struct _reent *reent)
+{
+       reent->_errno = ENOSYS;
+       return -1;
+}
+
+/* fstat: not implemented, all devices are character devices */
+int
+_fstat_r(__attribute__((unused)) struct _reent *reent,
+        __attribute__((unused)) int fildes,
+        struct stat *st)
+{
+       st->st_mode = S_IFCHR;
+       return 0;
+}
+
+/* getpid: not implemented */
+int
+_getpid_r(struct _reent *reent)
+{
+       reent->_errno = ENOSYS;
+       return -1;
+}
+
+struct timeval;
+
+/* gettimeofday: not implemented */
+int
+_gettimeofday_r(struct _reent *reent,
+               __attribute__((unused)) struct timeval *ptimeval,
+               __attribute__((unused)) void *ptimezone)
+{
+       reent->_errno = ENOSYS;
+       return -1;
+}
+
+/* isatty; not implemented */
+int
+_isatty_r(struct _reent *reent, __attribute__((unused)) int file)
+{
+       reent->_errno = ENOTTY;
+       return 0;
+}
+
+/* kill a process: not implemented */
+int
+_kill_r(struct _reent *reent,
+       __attribute__((unused)) int pid,
+       __attribute__((unused)) int sig)
+{
+       reent->_errno = ENOSYS;
+       return -1;
+}
+
+/* link: not implemented */
+int
+_link_r(struct _reent *reent,
+       __attribute__((unused)) const char *existing,
+       __attribute__((unused)) const char *new)
+{
+       reent->_errno = EMLINK;
+       return -1;
+}
+
+/* lseek: not implemented */
+_off_t
+_lseek_r(__attribute__((unused)) struct _reent *reent,
+        __attribute__((unused)) int file,
+        __attribute__((unused)) _off_t ptr,
+        __attribute__((unused)) int dir)
+{
+       return 0;
+}
+
+/* readlink: not implemented */
+int
+_readlink_r(struct _reent *reent,
+           __attribute__((unused)) const char *path,
+           __attribute__((unused)) char *buf,
+           __attribute__((unused)) size_t bufsize)
+{
+       reent->_errno = ENOSYS;
+       return -1;
+}
+
+/* simple sbrk */
+void *
+_sbrk_r(__attribute__((unused)) struct _reent *reent, ptrdiff_t incr)
+{
+       static char *heap_end = NULL;
+       char *prev_heap_end;
+
+       if (heap_end == NULL)
+               heap_end = &_ebss;
+
+       prev_heap_end = heap_end;
+       heap_end += incr;
+       return (caddr_t)prev_heap_end;
+}
+
+/* stat: not implemented, all devices are character devices */
+int
+_stat_r(__attribute__((unused)) struct _reent *reent,
+       __attribute__((unused)) const char *file,
+       struct stat *st)
+{
+       st->st_mode = S_IFCHR;
+       return 0;
+}
+
+/* symlink: not implemented */
+int
+_symlink_r(struct _reent *reent,
+          __attribute__((unused)) const char *path1,
+          __attribute__((unused)) const char *path2)
+{
+       reent->_errno = ENOSYS;
+       return -1;
+}
+
+/* times: not timplemented */
+clock_t
+_times_r(struct _reent *reent,
+        __attribute__((unused)) struct tms *buf)
+{
+       reent->_errno = ENOSYS;
+       return -1;
+}
+
+/* unlink: not implemented */
+int
+_unlink_r(struct _reent *reent,
+         __attribute__((unused)) const char *name)
+{
+       reent->_errno = EMLINK;
+       return -1;
+}
+
+/* wait: not implemented */
+int
+_wait_r(struct _reent *reent, __attribute__((unused)) int *status)
+{
+       reent->_errno = ENOSYS;
+       return -1;
+}
+
diff --git a/lib/uart/include/ucg_uart.h b/lib/uart/include/ucg_uart.h
new file mode 100644 (file)
index 0000000..ae50e78
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2005-2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCG_UART_H_
+#define UCG_UART_H_
+
+#include <ucg_cirbuf.h>
+
+/**
+ * UART interface, using interrupts. Supports 5 to 8 bits characters
+ * (not 9 bits).
+ */
+
+/**
+ * Callback type of tx/rx event.
+ */
+typedef void (ucg_uart_cb_t)(char);
+
+struct ucg_uart;
+
+/**
+ * Configuration of the uart
+ */
+struct ucg_uart_config {
+       uint8_t enable     : 1, /**< enable or disable the uart */
+               parity     : 2, /**< none, odd or even */
+               stop_bits  : 1, /**< 1 or 2 bits at the end of the frame */
+               reserved   : 4; /**< nothing for now */
+       uint8_t nbits;          /**< number of bits in frame, 5, 6, 7 or 8 */
+       uint32_t baudrate;      /**< speed of uart */
+};
+
+/**
+ * Per-arch uart driver operations
+ */
+struct ucg_uart_driver_ops {
+       void (*disable_tx_irq)(struct ucg_uart *uart);  /**< Disable tx intrpt */
+       void (*enable_tx_irq)(struct ucg_uart *uart);   /**< Enable tx intrpt */
+       uint8_t (*tx_ready)(struct ucg_uart *uart);     /**< Test if tx register empty */
+       uint8_t (*rx_ready)(struct ucg_uart *uart);     /**< Test if rx register full */
+       void (*set_udr)(struct ucg_uart *uart, char c); /**< Set uart data register */
+       char (*get_udr)(struct ucg_uart *uart);         /**< Get uart data register */
+       int (*set_conf)(struct ucg_uart *uart,
+               const struct ucg_uart_config *conf);    /**< Set configuration */
+};
+
+/**
+ * Generic Uart structure
+ */
+struct ucg_uart {
+       struct ucg_uart_config conf;            /**< Current configuration */
+       ucg_uart_cb_t *rx_cb;                   /**< RX callback */
+       ucg_uart_cb_t *tx_cb;                   /**< TX callback */
+       struct ucg_cirbuf *rx_fifo;             /**< RX fifo */
+       struct ucg_cirbuf *tx_fifo;             /**< TX fifo */
+       const struct ucg_uart_driver_ops *ops;  /**< Pointer to arch uart ops */
+       void *driver_data;                      /**< Opaque arch uart data */
+};
+
+/**
+ * Uart wait directive, used in rx/tx functions.
+ */
+enum ucg_uart_wait {
+       WAIT,     /**< Loop until operation can be done */
+       NOWAIT,   /**< Return if operation can't be done now */
+};
+
+/**
+ * Handle TX register empty interrupt
+ *
+ * @param uart
+ *   The uart structure.
+ */
+void ucg_uart_tx_intr(struct ucg_uart *uart);
+
+/**
+ * Handle RX register empty interrupt
+ *
+ * @param uart
+ *   The uart structure.
+ */
+void ucg_uart_rx_intr(struct ucg_uart *uart);
+
+/**
+ * Initialize uart
+ *
+ * Initialize the per-arch and generic uart structure, the rx and tx
+ * fifos, and configure the uart with the default configuration.
+ *
+ * @param uart
+ *   The uart structure.
+ * @param ops
+ *   A pointer to the per-arch uart operations.
+ * @param driver_data
+ *   A pointer to a per-arch uart data structure (ex: struct ucg_stm32_uart *)
+ * @param rx_cbuf
+ *   A pointer to an uninitialized cirbuf, used for rx fifo
+ * @param rx_buf
+ *   The buffer used by the rx fifo
+ * @param rx_bufsize
+ *   The length of rx_buf in bytes
+ * @param tx_cbuf
+ *   A pointer to an uninitialized cirbuf, used for tx fifo
+ * @param tx_buf
+ *   The buffer used by the tx fifo
+ * @param tx_bufsize
+ *   The length of tx_buf in bytes
+ * @return
+ *   0 on success, negative on error.
+ */
+int ucg_uart_init(struct ucg_uart *uart,
+       const struct ucg_uart_driver_ops *ops, void *driver_data,
+       struct ucg_cirbuf *rx_cbuf, char *rx_buf, unsigned rx_bufsize,
+       struct ucg_cirbuf *tx_cbuf, char *tx_buf, unsigned tx_bufsize);
+
+/**
+ * Configure the uart with the given configuration.
+ *
+ * The function ucg_uart_init() must have been called first.
+ *
+ * @param uart
+ *   The uart structure.
+ * @param conf
+ *   The configuration to be applied.
+ * @return
+ *   0 on success, negative on error.
+ */
+int ucg_uart_setconf(struct ucg_uart *uart, const struct ucg_uart_config *conf);
+
+/**
+ * Get the current configuration of the uart.
+ *
+ * @param uart
+ *   The uart structure.
+ * @param conf
+ *   The pointer to be filled with the current running configuration.
+ */
+void ucg_uart_getconf(struct ucg_uart *uart, struct ucg_uart_config *conf);
+
+/**
+ * Receive a character.
+ *
+ * Receive the next character, taken from the fifo if any. If NOWAIT is
+ * given, the function returns an error if the fifo is empty. If WAIT is
+ * given, the function loops until a new character can be returned.
+ *
+ * The function can be called from an interrupt, but it should be used
+ * with care if WAIT is given, as it will loop until a character is
+ * received.
+ *
+ * @param uart
+ *   The uart structure.
+ * @param wait
+ *   The wait directive (WAIT or NOWAIT)
+ * @return
+ *   Return the character on success (>= 0), or a negative value on error.
+ */
+int ucg_uart_recv(struct ucg_uart *uart, enum ucg_uart_wait wait);
+
+/**
+ * Send a character.
+ *
+ * Put a new character in the TX fifo. If NOWAIT is given, the function
+ * returns an error if the fifo is full. If WAIT is given, the function
+ * loops until some room is available in the tx fifo.
+ *
+ * The function can be called from an interrupt, but it should be used
+ * with care if WAIT is given, as it will wait that the current
+ * transmission is finished before returning.
+ *
+ * @param uart
+ *   The uart structure.
+ * @param wait
+ *   The wait directive (WAIT or NOWAIT)
+ * @return
+ *   Return the transmitted character on success (>= 0), or a negative
+ *   value on error.
+ */
+int ucg_uart_send(struct ucg_uart *uart, char c, enum ucg_uart_wait wait);
+
+/**
+ * Flush the TX fifo
+ *
+ * Loop until the uart TX fifo is empty. The function can be called from
+ * an interrupt, but it should be used with care as it will wait that the
+ * transmission of all bytes of the fifo is finished before returning.
+ *
+ * @param uart
+ *   The uart structure.
+ */
+void ucg_uart_flush(struct ucg_uart *uart);
+
+/**
+ * Register a TX callback function
+ *
+ * Register a function that will be called after a character is written
+ * in the UART dta register. This function is called with interrupts
+ * locked, and the user is free to access or modify the tx fifo if
+ * required. Note that the character that has just been transmitted is
+ * not in the tx fifo when the callback is invoked.
+ *
+ * @param uart
+ *   The uart structure.
+ * @param f
+ *   The function to call on tx event, can be NULL to disable.
+ * @param arg
+ *   The opaque argument given to the function.
+ */
+void ucg_uart_register_tx_cb(struct ucg_uart *uart, ucg_uart_cb_t *f);
+
+/**
+ * Register a TX callback function
+ *
+ * Register a function that will be called after a character is read
+ * from the UART dta register. This function is called with interrupts
+ * locked, and the user is free to access or modify the rx fifo if
+ * required. Note that the character that has just been received is
+ * present in the rx fifo when the callback is invoked.
+ *
+ * @param uart
+ *   The uart structure.
+ * @param f
+ *   The function to call on tx event, can be NULL to disable.
+ * @param arg
+ *   The opaque argument given to the function.
+ */
+void ucg_uart_register_rx_cb(struct ucg_uart *uart, ucg_uart_cb_t *f);
+
+#endif /* UCG_UART_H_ */
diff --git a/lib/uart/ucg_uart.c b/lib/uart/ucg_uart.c
new file mode 100644 (file)
index 0000000..68d4d2f
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2005-2015, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#include <ucg_irq.h>
+#include <ucg_cirbuf.h>
+#include <ucg_uart.h>
+
+/* Send the next char, or disable tx interruptions if tx fifo is
+ * empty. Must be called with intrp locked and uart data register
+ * empty. */
+static void ucg_uart_send_next_char(struct ucg_uart *uart)
+{
+       char c;
+
+       if (ucg_cirbuf_is_empty(uart->tx_fifo)) {
+               uart->ops->disable_tx_irq(uart);
+               return;
+       }
+
+       c = ucg_cirbuf_get_tail(uart->tx_fifo);
+       ucg_cirbuf_del_tail(uart->tx_fifo);
+       uart->ops->set_udr(uart, c);
+       if (uart->tx_cb != NULL)
+               uart->tx_cb(c);
+
+       uart->ops->enable_tx_irq(uart);
+
+}
+
+/* Receive char and put it in the fifo. If the rx fifo is full, the char
+ * is dropped. Must be called with intrp locked and uart data register
+ * empty. */
+static void ucg_uart_recv_next_char(struct ucg_uart *uart)
+{
+       char c;
+
+       c = uart->ops->get_udr(uart);
+
+       if (!ucg_cirbuf_is_full(uart->rx_fifo))
+               ucg_cirbuf_add_head(uart->rx_fifo, c);
+
+       if (uart->rx_cb != NULL)
+               uart->rx_cb(c);
+}
+
+/* called by user to handle the "tx register empty" interrupt */
+void ucg_uart_tx_intr(struct ucg_uart *uart)
+{
+       ucg_uart_send_next_char(uart);
+}
+
+/* called by user to handle the "rx register non empty" interrupt */
+void ucg_uart_rx_intr(struct ucg_uart *uart)
+{
+       ucg_uart_recv_next_char(uart);
+}
+
+
+/* get a char from the receive fifo */
+static int ucg_uart_recv_nowait(struct ucg_uart *uart)
+{
+       char c = 0;
+       ucg_irqflags_t flags;
+
+       flags = ucg_irq_lock_save();
+       if (ucg_cirbuf_is_empty(uart->rx_fifo)) {
+               ucg_irq_unlock_restore(flags);
+               return -1;
+       }
+
+       c = ucg_cirbuf_get_tail(uart->rx_fifo);
+       ucg_cirbuf_del_tail(uart->rx_fifo);
+       ucg_irq_unlock_restore(flags);
+
+       return (int)c;
+}
+
+/* get a char from the receive fifo */
+int ucg_uart_recv(struct ucg_uart *uart, enum ucg_uart_wait wait)
+{
+       int c = 0;
+
+       c = ucg_uart_recv_nowait(uart);
+       if (c >= 0)
+               return c;
+
+       if (wait == NOWAIT)
+               return -1;
+
+       if (ucg_irq_locked()) {
+               /* if irq are masked we have to poll uart register */
+               while (!uart->ops->rx_ready(uart))
+                       ;
+
+               /* then receive the data, and return it */
+               ucg_uart_recv_next_char(uart);
+               c = ucg_uart_recv_nowait(uart);
+       } else {
+               while ((c = ucg_uart_recv_nowait(uart)) == -1)
+                       ;
+       }
+
+       return c;
+}
+
+/* send a char, or put it in the fifo if uart is not ready. Return -1
+ * if fifo is full */
+static int ucg_uart_send_nowait(struct ucg_uart *uart, char c)
+{
+       ucg_irqflags_t flags;
+
+       flags = ucg_irq_lock_save();
+
+       if (ucg_cirbuf_is_full(uart->tx_fifo)) {
+               ucg_irq_unlock_restore(flags);
+               return -1;
+       }
+
+       /* uart is ready to send */
+       if (ucg_cirbuf_is_empty(uart->tx_fifo) && uart->ops->tx_ready(uart)) {
+               uart->ops->set_udr(uart, c);
+               if (uart->tx_cb != NULL)
+                       uart->tx_cb(c);
+               uart->ops->enable_tx_irq(uart);
+
+       }
+       else { /* not ready, put char in fifo */
+               ucg_cirbuf_add_head(uart->tx_fifo, c);
+       }
+
+       ucg_irq_unlock_restore(flags);
+       return 0;
+}
+
+/* send a byte */
+int ucg_uart_send(struct ucg_uart *uart, char c, enum ucg_uart_wait wait)
+{
+       /* try to send the char */
+       if (ucg_uart_send_nowait(uart, c) == 0)
+               return 0;
+
+       if (wait == NOWAIT)
+               return -1;
+
+       if (ucg_irq_locked()) {
+               /* if irq are masked we have to poll uart register */
+               while (!uart->ops->tx_ready(uart))
+                       ;
+
+               /* then send a data to free a room in the fifo */
+               ucg_uart_send_next_char(uart);
+               ucg_cirbuf_add_head(uart->tx_fifo, c);
+       } else {
+               /* if irq are not locked, we can loop to emit */
+               while (ucg_uart_send_nowait(uart, c) != 0)
+                       ;
+       }
+       return 0;
+}
+
+/* flush the tx fifo */
+void ucg_uart_flush(struct ucg_uart *uart)
+{
+       ucg_irqflags_t flags;
+
+       if (ucg_irq_locked()) {
+               /* poll uart register, and send next byte until tx fifo
+                * is empty */
+               while (1) {
+                       if (ucg_cirbuf_is_empty(uart->tx_fifo))
+                               break;
+                       while (!uart->ops->tx_ready(uart))
+                               ;
+                       ucg_uart_send_next_char(uart);
+               }
+       } else {
+               /* just wait that tx fifo is empty */
+               while (1) {
+                       flags = ucg_irq_lock_save();
+                       if (ucg_cirbuf_is_empty(uart->tx_fifo)) {
+                               ucg_irq_unlock_restore(flags);
+                               break;
+                       }
+                       ucg_irq_unlock_restore(flags);
+               }
+       }
+}
+
+/* read the current running configuration */
+void ucg_uart_getconf(struct ucg_uart *uart, struct ucg_uart_config *conf)
+{
+       memcpy(conf, &uart->conf, sizeof(*conf));
+}
+
+/* set a new uart config */
+int ucg_uart_setconf(struct ucg_uart *uart, const struct ucg_uart_config *conf)
+{
+       int ret;
+
+       ret = uart->ops->set_conf(uart, conf);
+       if (ret < 0)
+               return ret;
+
+       memcpy(&uart->conf, conf, sizeof(*conf));
+       return 0;
+}
+
+/* register the function that will be executed at each byte transmission */
+void ucg_uart_register_tx_cb(struct ucg_uart *uart, void (*f)(char))
+{
+       ucg_irqflags_t flags;
+
+       flags = ucg_irq_lock_save();
+       uart->tx_cb = f;
+       ucg_irq_unlock_restore(flags);
+}
+
+/* register the function that will be executed at each byte reception */
+void ucg_uart_register_rx_cb(struct ucg_uart *uart, void (*f)(char))
+{
+       ucg_irqflags_t flags;
+
+       flags = ucg_irq_lock_save();
+       uart->rx_cb = f;
+       ucg_irq_unlock_restore(flags);
+}
+
+/* init uart and fifos, call the per-arch initialization and set a default
+ * configuration (9600 bauds) */
+int ucg_uart_init(struct ucg_uart *uart,
+       const struct ucg_uart_driver_ops *ops, void *driver_data,
+       struct ucg_cirbuf *rx_cbuf, char *rx_buf, unsigned rx_bufsize,
+       struct ucg_cirbuf *tx_cbuf, char *tx_buf, unsigned tx_bufsize)
+{
+       int ret;
+
+       const struct ucg_uart_config def_conf = {
+               .enable = 1,
+               .parity = 0,
+               .stop_bits = 0,
+               .reserved = 0,
+               .nbits = 8,
+               .baudrate = 9600,
+       };
+
+       memset(uart, 0, sizeof(*uart));
+       ucg_cirbuf_init(rx_cbuf, rx_buf, 0, rx_bufsize);
+       ucg_cirbuf_init(tx_cbuf, tx_buf, 0, tx_bufsize);
+       uart->rx_fifo = rx_cbuf;
+       uart->tx_fifo = tx_cbuf;
+       uart->ops = ops;
+       uart->driver_data = driver_data;
+       ret = ucg_uart_setconf(uart, &def_conf);
+
+       return ret;
+}
diff --git a/mk/ucgine-ar-rules.mk b/mk/ucgine-ar-rules.mk
new file mode 100644 (file)
index 0000000..4ad30cb
--- /dev/null
@@ -0,0 +1,55 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# dump some infos if debug is enabled
+ifeq ($(D),1)
+$(call disp_list,------ all-ar,$(all-ar))
+$(foreach ar,$(all-ar),\
+       $(info,out-$(ar): $(out-$(ar))) \
+       $(call disp_list,pre-$(ar),$(pre-$(ar))) \
+)
+endif
+
+# include dependencies and commands files if they exist
+$(foreach ar,$(all-ar),\
+       $(eval -include $(call depfile,$(ar))) \
+       $(eval -include $(call cmdfile,$(ar))) \
+)
+
+# remove duplicates
+filtered-all-ar := $(sort $(all-ar))
+
+# link several objects files into one shared object
+$(filtered-all-ar): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+       @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+       @$(call display_deps,$(pre-$(@)),$@,\
+               $(call ar_cmd,$(pre-$(@)),$@),$?)
+       @$(if $(call check_deps,$@,$(call ar_cmd,$(pre-$(@)),$@),$?),\
+               $(call ar_print_cmd,$(pre-$(@)),$@) && \
+               $(call ar_cmd,$(pre-$(@)),$@) && \
+               $(call save_cmd,$(call ar_cmd,$(pre-$(@)),$@),$@) && \
+               $(call create_empty_depfile,$@))
diff --git a/mk/ucgine-ar-vars.mk b/mk/ucgine-ar-vars.mk
new file mode 100644 (file)
index 0000000..7d2fab7
--- /dev/null
@@ -0,0 +1,75 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# ar-y-$(ar) is provided by the user
+#   $(ar) is the path of the static library, and the variable contains
+#   the list of sources. Several ar-y-$(ar) can be present.
+
+# list all ar builds requested by user
+all-ar := $(patsubst ar-y-%,%,$(filter ar-y-%,$(.VARIABLES)))
+
+# add them to the list of targets
+all-targets += $(all-ar)
+
+# for each ar, create the following variables:
+#   out-$(ar) = output path of the arcutable
+#   pre-$(ar) = list of prerequisites for this arcutable
+# Some source files need intermediate objects, we define these variables
+# for them too, and add them in a list: $(all-iobj).
+# Last, we add the generated files in $(all-clean-file).
+$(foreach ar,$(all-ar),\
+       $(eval out-$(ar) := $(dir $(ar))) \
+       $(eval pre-$(ar) := ) \
+       $(foreach src,$(ar-y-$(ar)), \
+               $(if $(call is_cc_source,$(src)), \
+                       $(eval iobj := $(call src2iobj,$(src),$(out-$(ar)))) \
+                       $(eval pre-$(iobj) := $(src)) \
+                       $(eval all-iobj += $(iobj)) \
+                       $(eval all-clean-file += $(iobj)) \
+                       $(eval pre-$(ar) += $(iobj)) \
+               , \
+               $(if $(call is_obj_source,$(src)),\
+                       $(eval pre-$(ar) += $(src)) \
+               , \
+               $(error "unsupported source format: $(src)"))) \
+       )\
+       $(eval all-clean-file += $(ar)) \
+)
+
+# link several *.o files into a static libary
+#   $1: sources (*.o)
+#   $2: dst (xyz.a)
+ar_cmd = ar crsD $(2) $(1)
+
+# print line used to ar object files
+ifeq ($(V),1)
+ar_print_cmd = echo $(call protect_quote,$(call ar_cmd,$1,$2))
+else
+ar_print_cmd = echo "  AR $(2)"
+endif
+
+all-clean-file += $(all-ar)
diff --git a/mk/ucgine-clean-rules.mk b/mk/ucgine-clean-rules.mk
new file mode 100644 (file)
index 0000000..6bdf867
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+.PHONY: _ucgine_clean
+_ucgine_clean: $(all-clean-target) FORCE
+       @$(call clean_print_cmd,$(all-clean-file) $(call depfile,$(all-clean-file)) \
+               $(call cmdfile,$(all-clean-file))) && \
+       $(call clean_cmd,$(all-clean-file) $(call depfile,$(all-clean-file)) \
+               $(call cmdfile,$(all-clean-file)))
diff --git a/mk/ucgine-clean-vars.mk b/mk/ucgine-clean-vars.mk
new file mode 100644 (file)
index 0000000..53f732e
--- /dev/null
@@ -0,0 +1,37 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# remove files
+#  $1: files
+clean_cmd = rm -rf $(1)
+
+# print line used to clean files
+ifeq ($(V),1)
+clean_print_cmd = echo $(call protect_quote,$(call clean_cmd,$1))
+else
+clean_print_cmd = echo "  CLEAN $(CURDIR)"
+endif
diff --git a/mk/ucgine-copy-rules.mk b/mk/ucgine-copy-rules.mk
new file mode 100644 (file)
index 0000000..6602a9e
--- /dev/null
@@ -0,0 +1,55 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# dump some infos if debug is enabled
+ifeq ($(D),1)
+$(call disp_list,------ all-copy,$(all-copy))
+$(foreach copy,$(all-copy),\
+       $(info,out-$(copy): $(out-$(copy))) \
+       $(call disp_list,pre-$(copy),$(pre-$(copy))) \
+)
+endif
+
+# include dependencies and commands files if they exist
+$(foreach copy,$(all-copy),\
+       $(eval -include $(call depfile,$(copy))) \
+       $(eval -include $(call cmdfile,$(copy))) \
+)
+
+# remove duplicates
+filtered-all-copy := $(sort $(all-copy))
+
+# convert format of executable
+$(filtered-all-copy): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+       @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+       @$(call display_deps,$(pre-$(@)),$@,\
+               $(call copy_cmd,$(pre-$(@)),$@),$?)
+       @$(if $(call check_deps,$@,$(call copy_cmd,$(pre-$(@)),$@),$?),\
+               $(call copy_print_cmd,$(pre-$(@)),$@) && \
+               $(call copy_cmd,$(pre-$(@)),$@) && \
+               $(call save_cmd,$(call copy_cmd,$(pre-$(@)),$@),$@) && \
+               $(call create_empty_depfile,$@))
diff --git a/mk/ucgine-copy-vars.mk b/mk/ucgine-copy-vars.mk
new file mode 100644 (file)
index 0000000..677274e
--- /dev/null
@@ -0,0 +1,74 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# copy a file
+# copy-y-$(copy) is provided by the user
+#   $(copy) is the path of the directory containing the destination
+#   files, and the variable contains the path of the files to copy. Several
+#   copy-y-$(copy) can be present.
+
+# list all path requested by user
+_all-copy := $(patsubst copy-y-%,%,$(filter copy-y-%,$(.VARIABLES)))
+all-copy :=
+
+# for each copy, create the following variables:
+#   out-$(copy) = output path of the executable
+#   pre-$(copy) = list of prerequisites for this executable
+# We also add the files in $(all-copy).
+$(foreach copy,$(_all-copy),\
+       $(if $(notdir $(copy)), \
+               $(if $(call compare,$(words $(copy-y-$(copy))),1), \
+                       $(error "only one source file is allowed in copy-y-$(copy)")) \
+               $(eval dst := $(dir $(copy))$(notdir $(copy-y-$(copy)))) \
+               $(eval out-$(copy) := $(dir $(copy))) \
+               $(eval pre-$(copy) := $(copy-y-$(copy))) \
+               $(eval all-copy += $(dst)) \
+       , \
+               $(foreach src,$(copy-y-$(copy)),\
+                       $(eval dst := $(copy)$(notdir $(src))) \
+                       $(eval out-$(copy) := $(copy)) \
+                       $(eval pre-$(dst) := $(src)) \
+                       $(eval all-copy += $(dst)) \
+               ) \
+       ) \
+)
+
+# add them to the list of targets and clean
+all-targets += $(all-copy)
+all-clean-file += $(all-copy)
+
+# convert format of executable from elf to ihex
+#   $1: source executable (elf)
+#   $2: destination file
+copy_cmd = $(CP) $(1) $(2)
+
+# print line used to convert executable format
+ifeq ($(V),1)
+copy_print_cmd = echo $(call protect_quote,$(call copy_cmd,$1,$2))
+else
+copy_print_cmd = echo "  COPY $(2)"
+endif
diff --git a/mk/ucgine-exe-rules.mk b/mk/ucgine-exe-rules.mk
new file mode 100644 (file)
index 0000000..a1a5c42
--- /dev/null
@@ -0,0 +1,55 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# dump some infos if debug is enabled
+ifeq ($(D),1)
+$(call disp_list,------ all-exe,$(all-exe))
+$(foreach exe,$(all-exe),\
+       $(info,out-$(exe): $(out-$(exe))) \
+       $(call disp_list,pre-$(exe),$(pre-$(exe))) \
+)
+endif
+
+# include dependencies and commands files if they exist
+$(foreach exe,$(all-exe),\
+       $(eval -include $(call depfile,$(exe))) \
+       $(eval -include $(call cmdfile,$(exe))) \
+)
+
+# remove duplicates
+filtered-all-exe := $(sort $(all-exe))
+
+# link several objects files into one executable
+$(filtered-all-exe): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+       @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+       @$(call display_deps,$(pre-$(@)),$@,\
+               $(call link_cmd,$(pre-$(@)),$@),$?)
+       @$(if $(call check_deps,$@,$(call link_cmd,$(pre-$(@)),$@),$?),\
+               $(call link_print_cmd,$(pre-$(@)),$@) && \
+               $(call link_cmd,$(pre-$(@)),$@) && \
+               $(call save_cmd,$(call link_cmd,$(pre-$(@)),$@),$@) && \
+               $(call create_empty_depfile,$@))
diff --git a/mk/ucgine-exe-vars.mk b/mk/ucgine-exe-vars.mk
new file mode 100644 (file)
index 0000000..f980be4
--- /dev/null
@@ -0,0 +1,79 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# exe-y-$(exe) is provided by the user
+#   $(exe) is the path of the binary, and the variable contains
+#   the list of sources. Several exe-y-$(exe) can be present.
+
+# list all exe builds requested by user
+all-exe := $(patsubst exe-y-%,%,$(filter exe-y-%,$(.VARIABLES)))
+
+# add them to the list of targets
+all-targets += $(all-exe)
+
+# for each exe, create the following variables:
+#   out-$(exe) = output path of the executable
+#   pre-$(exe) = list of prerequisites for this executable
+# Some source files need intermediate objects, we define these variables
+# for them too, and add them in a list: $(all-iobj).
+# Last, we add the generated files in $(all-clean-file).
+$(foreach exe,$(all-exe),\
+       $(eval out-$(exe) := $(dir $(exe))) \
+       $(eval pre-$(exe) := ) \
+       $(foreach src,$(exe-y-$(exe)), \
+               $(if $(call is_cc_source,$(src)), \
+                       $(eval iobj := $(call src2iobj,$(src),$(out-$(exe)))) \
+                       $(eval pre-$(iobj) := $(src)) \
+                       $(eval all-iobj += $(iobj)) \
+                       $(eval all-clean-file += $(iobj)) \
+                       $(eval pre-$(exe) += $(iobj)) \
+               , \
+               $(if $(call is_obj_source,$(src)),\
+                       $(eval pre-$(exe) += $(src)) \
+               , \
+               $(if $(call is_alib_source,$(src)),\
+                       $(eval pre-$(exe) += $(src)) \
+               , \
+               $(error "unsupported source format: $(src)")))) \
+       )\
+       $(eval all-clean-file += $(exe)) \
+)
+
+# link several *.o files into a exeary
+#   $1: sources (*.o) (*.a)
+#   $2: dst (xyz.o too)
+link_cmd = $(CC) $(LDFLAGS) $(ldflags-$(2)) -o $(2) $(filter %.o,$(1)) \
+        $(filter %.a,$(1)) $(LDLIBS) $(ldlibs-$(2))
+
+# print line used to link object files
+ifeq ($(V),1)
+link_print_cmd = echo $(call protect_quote,$(call link_cmd,$1,$2))
+else
+link_print_cmd = echo "  EXE $(2)"
+endif
+
+all-clean-file += $(all-exe)
diff --git a/mk/ucgine-obj-rules.mk b/mk/ucgine-obj-rules.mk
new file mode 100644 (file)
index 0000000..af208e4
--- /dev/null
@@ -0,0 +1,83 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# dump some infos if debug is enabled
+ifeq ($(D),1)
+$(call disp_list,------ all-obj,$(all-obj))
+$(foreach obj,$(all-obj),\
+       $(info,out-$(obj): $(out-$(obj))) \
+       $(call disp_list,pre-$(obj),$(pre-$(obj))) \
+)
+$(call disp_list,------ all-iobj,$(all-iobj))
+$(foreach iobj,$(all-iobj),\
+       $(call disp_list,pre-$(iobj),$(pre-$(iobj))) \
+)
+endif
+
+# if a generated file has the same name than a user target,
+# generate an error
+conflicts := $(filter $(all-iobj),$(all-targets))
+$(if $(conflicts), \
+       $(error Intermediate file has the same names than user targets:\
+               $(conflicts)))
+
+# include dependencies and commands files if they exist
+$(foreach obj,$(all-obj),\
+       $(eval -include $(call depfile,$(obj))) \
+       $(eval -include $(call cmdfile,$(obj))) \
+)
+$(foreach iobj,$(all-iobj),\
+       $(eval -include $(call depfile,$(iobj))) \
+       $(eval -include $(call cmdfile,$(iobj))) \
+)
+
+# remove duplicates
+filtered-all-iobj := $(sort $(all-iobj))
+
+# convert source files to intermediate object file
+$(filtered-all-iobj): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+       @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+       @$(call display_deps,$(pre-$(@)),$@,$(call compile_cmd,$(pre-$(@)),$@),$?)
+       @$(if $(call check_deps,$@,$(call compile_cmd,$(pre-$(@)),$@),$?),\
+               $(call compile_print_cmd,$(pre-$(@)),$@) && \
+               $(call compile_cmd,$(pre-$(@)),$@) && \
+               $(call save_cmd,$(call compile_cmd,$(pre-$(@)),$@),$@) && \
+               $(call obj-fixdep,$@))
+
+# remove duplicates
+filtered-all-obj := $(sort $(all-obj))
+
+# combine several objects files to one
+$(filtered-all-obj): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+       @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+       @$(call display_deps,$(pre-$(@)),$@,\
+               $(call combine_cmd,$(pre-$(@)),$@),$?)
+       @$(if $(call check_deps,$@,$(call combine_cmd,$(pre-$(@)),$@),$?),\
+               $(call combine_print_cmd,$(pre-$(@)),$@) && \
+               $(call combine_cmd,$(pre-$(@)),$@) && \
+               $(call save_cmd,$(call combine_cmd,$(pre-$(@)),$@),$@) && \
+               $(call create_empty_depfile,$@))
diff --git a/mk/ucgine-obj-vars.mk b/mk/ucgine-obj-vars.mk
new file mode 100644 (file)
index 0000000..8bd9101
--- /dev/null
@@ -0,0 +1,124 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# obj-y-$(obj) is provided by the user
+#  $(obj) is the path of the object, and the variable contains
+#  the list of sources. Several obj-y-$(obj) can be present.
+
+# list all object builds requested by user
+all-obj := $(patsubst obj-y-%,%,$(filter obj-y-%,$(.VARIABLES)))
+
+# add them to the list of targets
+all-targets += $(all-obj)
+
+# convert source path to intermediate object path, and filter
+# objects from sources
+#  $1: list of source paths
+#  $2: output directory (including trailing slash)
+#  return: list of intermediate object paths
+src2iobj = $(addprefix $(filter-out ./,$(2)),$(notdir $(strip \
+       $(patsubst %.c,%.o,\
+       $(patsubst %.s,%.o,\
+       $(filter-out %.o,$(1)))))))
+
+# return the file if it matches a extension that is built with cc
+#   $1: source file
+is_cc_source = $(filter %.c %.s %S,$(1))
+
+# return the file if it's already an object file: in this case no
+# intermediate object is needed
+#   $1: source file
+is_obj_source = $(filter %.o,$(1))
+
+# return the file if it's a static library
+#   $1: source file
+is_alib_source = $(filter %.a,$(1))
+
+# for each obj, create the following variables:
+#   out-$(obj) = output path of the object
+#   pre-$(obj) = list of prerequisites for this object
+# Some source files need intermediate objects, we define these variables
+# for them too, and add them in a list: $(all-iobj).
+# Last, we add the generated files in $(all-clean-file).
+$(foreach obj,$(all-obj),\
+       $(eval out-$(obj) := $(dir $(obj))) \
+       $(eval pre-$(obj) := ) \
+       $(foreach src,$(obj-y-$(obj)), \
+               $(if $(call is_cc_source,$(src)), \
+                       $(eval iobj := $(call src2iobj,$(src),$(out-$(obj)))) \
+                       $(eval pre-$(iobj) := $(src)) \
+                       $(eval all-iobj += $(iobj)) \
+                       $(eval all-clean-file += $(iobj)) \
+                       $(eval pre-$(obj) += $(iobj)) \
+               , \
+               $(if $(call is_obj_source,$(src)),\
+                       $(eval pre-$(obj) += $(src)) \
+               , \
+               $(error "unsupported source format: $(src)"))) \
+       )\
+       $(eval all-clean-file += $(obj)) \
+)
+
+# fix the format of .o.d.tmp (generated by gcc) to a .o.d that defines
+# dependencies as makefile variables
+#  $1: object file (.o)
+obj-fixdep = if [ -f $(call file2tmpdep,$(1)) ]; then\
+               echo -n "dep-$(1) = " > $(call depfile,$(1)) && \
+               sed 's,^[^ ][^:]*: ,,' $(call file2tmpdep,$(1)) >> $(call depfile,$(1)) && \
+               rm -f $(call file2tmpdep,$(1)); \
+       else \
+               $(call create_empty_depfile,$(1)); \
+       fi
+
+# compile a file
+#  $1: sources
+#  $2: dst
+compile_cmd = $(CC) -Wp,-MD,$(call file2tmpdep,$(2)) \
+       $(CPPFLAGS) $(cppflags-$(2)) \
+       $(CFLAGS) $(cflags-$(2)) \
+       -c -o $2 $1
+
+# print line used to compile a file
+ifeq ($(V),1)
+compile_print_cmd = echo $(call protect_quote,$(call compile_cmd,$1,$2))
+else
+compile_print_cmd = echo "  CC $(2)"
+endif
+
+# combine several *.o files into one
+#   $1: sources (*.o)
+#   $2: dst (xyz.o too)
+combine_cmd = $(LD) -r $(1) -o $(2)
+
+# print line used to combine object files
+ifeq ($(V),1)
+combine_print_cmd = echo $(call protect_quote,$(call combine_cmd,$1,$2))
+else
+combine_print_cmd = echo "  LD $(2)"
+endif
+
+all-clean-file += $(all-obj)
diff --git a/mk/ucgine-objcopy-rules.mk b/mk/ucgine-objcopy-rules.mk
new file mode 100644 (file)
index 0000000..86a7cb6
--- /dev/null
@@ -0,0 +1,74 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# dump some infos if debug is enabled
+ifeq ($(D),1)
+$(call disp_list,------ all-objcopy-hex,$(all-objcopy-hex))
+$(foreach objcopy,$(all-objcopy-hex),\
+       $(info,out-$(objcopy): $(out-$(objcopy))) \
+       $(call disp_list,pre-$(objcopy),$(pre-$(objcopy))) \
+)
+$(call disp_list,------ all-objcopy-bin,$(all-objcopy-bin))
+$(foreach objcopy,$(all-objcopy-bin),\
+       $(info,out-$(objcopy): $(out-$(objcopy))) \
+       $(call disp_list,pre-$(objcopy),$(pre-$(objcopy))) \
+)
+endif
+
+# include dependencies and commands files if they exist
+$(foreach objcopy,$(all-objcopy-hex) $(all-objcopy-bin),\
+       $(eval -include $(call depfile,$(objcopy))) \
+       $(eval -include $(call cmdfile,$(objcopy))) \
+)
+
+# remove duplicates
+filtered-all-objcopy-hex := $(sort $(all-objcopy-hex))
+
+# convert format of executable
+$(filtered-all-objcopy-hex): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+       @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+       @$(call display_deps,$(pre-$(@)),$@,\
+               $(call objcopy_hex_cmd,$(pre-$(@)),$@),$?)
+       @$(if $(call check_deps,$@,$(call objcopy_hex_cmd,$(pre-$(@)),$@),$?),\
+               $(call objcopy_print_cmd,$(pre-$(@)),$@) && \
+               $(call objcopy_hex_cmd,$(pre-$(@)),$@) && \
+               $(call save_cmd,$(call objcopy_hex_cmd,$(pre-$(@)),$@),$@) && \
+               $(call create_empty_depfile,$@))
+
+# remove duplicates
+filtered-all-objcopy-bin := $(sort $(all-objcopy-bin))
+
+# convert format of executable
+$(filtered-all-objcopy-bin): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+       @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+       @$(call display_deps,$(pre-$(@)),$@,\
+               $(call objcopy_bin_cmd,$(pre-$(@)),$@),$?)
+       @$(if $(call check_deps,$@,$(call objcopy_bin_cmd,$(pre-$(@)),$@),$?),\
+               $(call objcopy_print_cmd,$(pre-$(@)),$@) && \
+               $(call objcopy_bin_cmd,$(pre-$(@)),$@) && \
+               $(call save_cmd,$(call objcopy_bin_cmd,$(pre-$(@)),$@),$@) && \
+               $(call create_empty_depfile,$@))
diff --git a/mk/ucgine-objcopy-vars.mk b/mk/ucgine-objcopy-vars.mk
new file mode 100644 (file)
index 0000000..d515d02
--- /dev/null
@@ -0,0 +1,89 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# objcopy changes the format of a binary
+# objcopy-hex-y-$(objcopy), objcopy-bin-y-$(objcopy) are provided by the user
+#   $(objcopy) is the path of the binary, and the variable contains
+#   the path to the elf. Several objcopy-y-$(objcopy) can be present.
+
+# list all executable builds requested by user
+all-objcopy-hex := $(patsubst objcopy-hex-y-%,%,$(filter objcopy-hex-y-%,$(.VARIABLES)))
+all-objcopy-bin := $(patsubst objcopy-bin-y-%,%,$(filter objcopy-bin-y-%,$(.VARIABLES)))
+
+# add them to the list of targets
+all-targets += $(all-objcopy-hex) $(all-objcopy-bin)
+
+# for each objcopy, create the following variables:
+#   out-$(objcopy) = output path of the executable
+#   pre-$(objcopy) = list of prerequisites for this executable
+# We also add the generated files in $(all-clean-file).
+$(foreach objcopy,$(all-objcopy-hex),\
+       $(if $(call compare,$(words $(objcopy-hex-y-$(objcopy))),1),\
+               $(error "only one source file is allowed in objcopy-hex-y-$(objcopy)")) \
+       $(eval out-$(objcopy) := $(dir $(objcopy))) \
+       $(eval pre-$(objcopy) := $(objcopy-hex-y-$(objcopy))) \
+       $(eval all-clean-file += $(objcopy)) \
+)
+
+# for each objcopy, create the following variables:
+#   out-$(objcopy) = output path of the executable
+#   pre-$(objcopy) = list of prerequisites for this executable
+# We also add the generated files in $(all-clean-file).
+$(foreach objcopy,$(all-objcopy-bin),\
+       $(if $(call compare,$(words $(objcopy-bin-y-$(objcopy))),1),\
+               $(error "only one source file is allowed in objcopy-bin-y-$(objcopy)")) \
+       $(eval out-$(objcopy) := $(dir $(objcopy))) \
+       $(eval pre-$(objcopy) := $(objcopy-bin-y-$(objcopy))) \
+       $(eval all-clean-file += $(objcopy)) \
+)
+
+# convert format of executable from elf to ihex
+#   $1: source executable (elf)
+#   $2: destination file
+objcopy_hex_cmd = $(OBJCOPY) -O ihex $(1) $(2)
+
+# print line used to convert executable format
+ifeq ($(V),1)
+objcopy_print_cmd = echo $(call protect_quote,$(call objcopy_hex_cmd,$1,$2))
+else
+objcopy_print_cmd = echo "  OBJCOPY $(2)"
+endif
+
+# convert format of executable from elf to binary
+#   $1: source executable (elf)
+#   $2: destination file
+objcopy_bin_cmd = $(OBJCOPY) -O binary $(1) $(2)
+
+# print line used to convert executable format
+ifeq ($(V),1)
+objcopy_print_cmd = echo $(call protect_quote,$(call objcopy_bin_cmd,$1,$2))
+else
+objcopy_print_cmd = echo "  OBJCOPY $(2)"
+endif
+
+# XXX dup ?
+all-clean-file += $(all-objcopy-hex) $(all-objcopy-bin)
diff --git a/mk/ucgine-post.mk b/mk/ucgine-post.mk
new file mode 100644 (file)
index 0000000..3d78dd6
--- /dev/null
@@ -0,0 +1,108 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# ---- variables that must be defined:
+#
+#  UCGINE: path to ucgine root
+#
+# ---- variable that can be defined anywhere
+#
+#  CROSS: prefix of the toolchain
+#  CP, LN, GAWK, GREP: coreutils tools
+#  CC, CPP, AR, LD, OBJCOPY, OBJDUMP, STRIP: compilers/binutils
+#
+# ---- variable that can be defined by Makefile:
+#
+#  obj-y-$(path)
+#  exe-y-$(path)
+#  ar-y-$(path)
+#  shlib-y-$(path)
+#  copy-y-$(path)
+#  slink-y-$(path)
+#  objcopy-y-$(path)
+#  subdir-y
+#
+#  CPPFLAGS, CFLAGS, LDFLAGS, LDLIBS: global flags
+#  cflags-$(path), cppflags-$(path), ldflags-$(path), ldlibs-$(path): per
+#    file flags
+#  mkflags-$(path): flags for subdirectories
+#
+# ---- variables that can be defined on the command line:
+#
+#  EXTRA_CPPFLAGS, EXTRA_CFLAGS, EXTRA_LDFLAGS, EXTRA_LDLIBS: global
+#    extra flags
+#  extra-cflags-$(path), extra-cppflags-$(path): per object extra flags
+
+ifeq ($(UCGINE),)
+$(error UCGINE environment variable is not defined)
+endif
+
+# list of targets asked by user
+all-targets :=
+# list of files generated
+all-clean-file :=
+
+# usual internal variables:
+#   out-$(file) = output path of a generated file
+#   pre-$(file) = list of files needed to generate $(file)
+#   all-type = list of targets for this type
+
+include $(UCGINE)/mk/ucgine-obj-vars.mk
+include $(UCGINE)/mk/ucgine-exe-vars.mk
+include $(UCGINE)/mk/ucgine-ar-vars.mk
+include $(UCGINE)/mk/ucgine-shlib-vars.mk
+include $(UCGINE)/mk/ucgine-copy-vars.mk
+include $(UCGINE)/mk/ucgine-slink-vars.mk
+include $(UCGINE)/mk/ucgine-objcopy-vars.mk
+include $(UCGINE)/mk/ucgine-subdir-vars.mk
+# must stay at the end
+include $(UCGINE)/mk/ucgine-clean-vars.mk
+
+# dump the list of targets
+ifeq ($(D),1)
+$(call disp_list,------ all-targets,$(all-targets))
+endif
+
+# first rule (default)
+.PHONY: _ucgine_all
+_ucgine_all: $(all-targets)
+
+# the includes below require second expansion
+.SECONDEXPANSION:
+
+include $(UCGINE)/mk/ucgine-obj-rules.mk
+include $(UCGINE)/mk/ucgine-exe-rules.mk
+include $(UCGINE)/mk/ucgine-ar-rules.mk
+include $(UCGINE)/mk/ucgine-shlib-rules.mk
+include $(UCGINE)/mk/ucgine-copy-rules.mk
+include $(UCGINE)/mk/ucgine-slink-rules.mk
+include $(UCGINE)/mk/ucgine-objcopy-rules.mk
+include $(UCGINE)/mk/ucgine-subdir-rules.mk
+include $(UCGINE)/mk/ucgine-clean-rules.mk
+
+.PHONY: FORCE
+FORCE:
diff --git a/mk/ucgine-pre.mk b/mk/ucgine-pre.mk
new file mode 100644 (file)
index 0000000..5ab0221
--- /dev/null
@@ -0,0 +1,49 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# ---- variables that must be defined:
+#
+#  UCGINE: path to ucgine root
+#  UCGINE_ARCH: architecture (ex: avr, stm32, ...)
+#
+
+ifeq ($(UCGINE),)
+$(error UCGINE environment variable is not defined)
+endif
+
+ifeq ($(UCGINE_ARCH),)
+$(error UCGINE_ARCH environment variable is not defined)
+endif
+
+MAKEFLAGS += --no-print-directory
+
+include $(UCGINE)/mk/ucgine-tools.mk
+
+include $(UCGINE)/arch/$(UCGINE_ARCH)/mk/ucgine-arch.mk
+
+include $(UCGINE)/mk/ucgine-vars.mk
+
diff --git a/mk/ucgine-shlib-rules.mk b/mk/ucgine-shlib-rules.mk
new file mode 100644 (file)
index 0000000..1a131cc
--- /dev/null
@@ -0,0 +1,55 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# dump some infos if debug is enabled
+ifeq ($(D),1)
+$(call disp_list,------ all-shlib,$(all-shlib))
+$(foreach shlib,$(all-shlib),\
+       $(info,out-$(shlib): $(out-$(shlib))) \
+       $(call disp_list,pre-$(shlib),$(pre-$(shlib))) \
+)
+endif
+
+# include dependencies and commands files if they exist
+$(foreach shlib,$(all-shlib),\
+       $(eval -include $(call depfile,$(shlib))) \
+       $(eval -include $(call cmdfile,$(shlib))) \
+)
+
+# remove duplicates
+filtered-all-shlib := $(sort $(all-shlib))
+
+# link several objects files into one shared object
+$(filtered-all-shlib): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+       @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+       @$(call display_deps,$(pre-$(@)),$@,\
+               $(call shlib_cmd,$(pre-$(@)),$@),$?)
+       @$(if $(call check_deps,$@,$(call shlib_cmd,$(pre-$(@)),$@),$?),\
+               $(call shlib_print_cmd,$(pre-$(@)),$@) && \
+               $(call shlib_cmd,$(pre-$(@)),$@) && \
+               $(call save_cmd,$(call shlib_cmd,$(pre-$(@)),$@),$@) && \
+               $(call create_empty_depfile,$@))
diff --git a/mk/ucgine-shlib-vars.mk b/mk/ucgine-shlib-vars.mk
new file mode 100644 (file)
index 0000000..7906510
--- /dev/null
@@ -0,0 +1,76 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# shlib-y-$(shlib) is provided by the user
+#   $(shlib) is the path of the shared library, and the variable
+#   contains the list of sources. Several shlib-y-$(shlib) can be
+#   present.
+
+# list all shlib builds requested by user
+all-shlib := $(patsubst shlib-y-%,%,$(filter shlib-y-%,$(.VARIABLES)))
+
+# add them to the list of targets
+all-targets += $(all-shlib)
+
+# for each shlib, create the following variables:
+#   out-$(shlib) = output path of the shlibcutable
+#   pre-$(shlib) = list of prerequisites for this shlibcutable
+# Some source files need intermediate objects, we define these variables
+# for them too, and add them in a list: $(all-iobj).
+# Last, we add the generated files in $(all-clean-file).
+$(foreach shlib,$(all-shlib),\
+       $(eval out-$(shlib) := $(dir $(shlib))) \
+       $(eval pre-$(shlib) := ) \
+       $(foreach src,$(shlib-y-$(shlib)), \
+               $(if $(call is_cc_source,$(src)), \
+                       $(eval iobj := $(call src2iobj,$(src),$(out-$(shlib)))) \
+                       $(eval pre-$(iobj) := $(src)) \
+                       $(eval all-iobj += $(iobj)) \
+                       $(eval all-clean-file += $(iobj)) \
+                       $(eval pre-$(shlib) += $(iobj)) \
+               , \
+               $(if $(call is_obj_source,$(src)),\
+                       $(eval pre-$(shlib) += $(src)) \
+               , \
+               $(error "unsupported source format: $(src)"))) \
+       )\
+       $(eval all-clean-file += $(shlib)) \
+)
+
+# link several *.o files into a shared libary
+#   $1: sources (*.o)
+#   $2: dst (xyz.so)
+shlib_cmd = $(CC) $(LDFLAGS) $(ldflags-$(2)) -shared -o $(2) $(1)
+
+# print line used to shlib object files
+ifeq ($(V),1)
+shlib_print_cmd = echo $(call protect_quote,$(call shlib_cmd,$1,$2))
+else
+shlib_print_cmd = echo "  SHLIB $(2)"
+endif
+
+all-clean-file += $(all-shlib)
diff --git a/mk/ucgine-slink-rules.mk b/mk/ucgine-slink-rules.mk
new file mode 100644 (file)
index 0000000..bea3aad
--- /dev/null
@@ -0,0 +1,55 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# dump some infos if debug is enabled
+ifeq ($(D),1)
+$(call disp_list,------ all-slink,$(all-slink))
+$(foreach slink,$(all-slink),\
+       $(info,out-$(slink): $(out-$(slink))) \
+       $(call disp_list,pre-$(slink),$(pre-$(slink))) \
+)
+endif
+
+# include dependencies and commands files if they exist
+$(foreach slink,$(all-slink),\
+       $(eval -include $(call depfile,$(slink))) \
+       $(eval -include $(call cmdfile,$(slink))) \
+)
+
+# remove duplicates
+filtered-all-slink := $(sort $(all-slink))
+
+# convert format of executable
+$(filtered-all-slink): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+       @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+       @$(call display_deps,$(pre-$(@)),$@,\
+               $(call slink_cmd,$(pre-$(@)),$@),$?)
+       @$(if $(call check_deps,$@,$(call slink_cmd,$(pre-$(@)),$@),$?),\
+               $(call slink_print_cmd,$(pre-$(@)),$@) && \
+               $(call slink_cmd,$(pre-$(@)),$@) && \
+               $(call save_cmd,$(call slink_cmd,$(pre-$(@)),$@),$@) && \
+               $(call create_empty_depfile,$@))
diff --git a/mk/ucgine-slink-vars.mk b/mk/ucgine-slink-vars.mk
new file mode 100644 (file)
index 0000000..428cb25
--- /dev/null
@@ -0,0 +1,74 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# create a symbolic link of a file
+# slink-y-$(slink) is provided by the user
+#   $(slink) is the path of the directory containing the destination
+#   files, and the variable contains the path of the files to linked. Several
+#   slink-y-$(slink) can be present.
+
+# list all path requested by user
+_all-slink := $(patsubst slink-y-%,%,$(filter slink-y-%,$(.VARIABLES)))
+all-slink :=
+
+# for each slink, create the following variables:
+#   out-$(slink) = output path of the executable
+#   pre-$(slink) = list of prerequisites for this executable
+# We also add the files in $(all-slink).
+$(foreach slink,$(_all-slink),\
+       $(if $(notdir $(slink)), \
+               $(if $(call compare,$(words $(slink-y-$(slink))),1), \
+                       $(error "only one source file is allowed in slink-y-$(slink)")) \
+               $(eval dst := $(dir $(slink))$(notdir $(slink-y-$(slink)))) \
+               $(eval out-$(slink) := $(dir $(slink))) \
+               $(eval pre-$(slink) := $(slink-y-$(slink))) \
+               $(eval all-slink += $(dst)) \
+       , \
+               $(foreach src,$(slink-y-$(slink)),\
+                       $(eval dst := $(slink)$(notdir $(src))) \
+                       $(eval out-$(slink) := $(slink)) \
+                       $(eval pre-$(dst) := $(src)) \
+                       $(eval all-slink += $(dst)) \
+               ) \
+       ) \
+)
+
+# add them to the list of targets and clean
+all-targets += $(all-slink)
+all-clean-file += $(all-slink)
+
+# convert format of executable from elf to ihex
+#   $1: source executable (elf)
+#   $2: destination file
+slink_cmd = $(LN) -nsf $(abspath $(1)) $(2)
+
+# print line used to convert executable format
+ifeq ($(V),1)
+slink_print_cmd = echo $(call protect_quote,$(call slink_cmd,$1,$2))
+else
+slink_print_cmd = echo "  SLINK $(2)"
+endif
diff --git a/mk/ucgine-subdir-rules.mk b/mk/ucgine-subdir-rules.mk
new file mode 100644 (file)
index 0000000..aae82db
--- /dev/null
@@ -0,0 +1,30 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+.PHONY: $(subdir-y)
+$(subdir-y): FORCE
+       $(Q)$(MAKE) -C $(@) $(mkflags-$(@)) $(MAKECMDGOALS)
diff --git a/mk/ucgine-subdir-vars.mk b/mk/ucgine-subdir-vars.mk
new file mode 100644 (file)
index 0000000..14e4ee1
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# subdir-y is provided by the user
+#  it contains the list of directory to build
+
+# add them to the list of targets
+all-targets += $(subdir-y)
+all-clean-target += $(subdir-y)
diff --git a/mk/ucgine-tools.mk b/mk/ucgine-tools.mk
new file mode 100644 (file)
index 0000000..484262b
--- /dev/null
@@ -0,0 +1,159 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+empty:=
+space:= $(empty) $(empty)
+indent:= $(space)$(space)
+
+# define a newline char, useful for debugging with $(info)
+define newline
+
+
+endef
+
+# $(prefix shell commands with $(Q) to silent them, except if V=1
+Q=@
+ifeq ("$(V)-$(origin V)", "1-command line")
+Q=
+endif
+
+# set variable $1 to $2 if the variable has an implicit value or
+# is not defined
+#  $1 variable name
+#  $2 new variable content
+set_default = $(if \
+       $(call not,$(or \
+               $(compare $(origin $(1)),default), \
+               $(compare $(origin $(1)),undefined) \
+       )),\
+       $(eval $(1) = $(2)) \
+)
+
+# display a list
+#  $1 title
+#  $2 list
+disp_list = $(info $(1)$(newline)\
+       $(addsuffix $(newline),$(addprefix $(space),$(2))))
+
+# add a dot in front of the file name
+#  $1 list of paths
+#  return: full paths with files prefixed by a dot
+dotfile = $(strip $(foreach f,$(1),\
+       $(join $(dir $f),.$(notdir $f))))
+
+# convert source/obj files into dot-dep filename
+#  $1 list of paths
+#  return: full paths with files prefixed by a dot and suffixed with .d
+depfile = $(strip $(call dotfile,$(addsuffix .d,$(1))))
+
+# convert source/obj files into dot-dep filename
+#  $1 list of paths
+#  return: full paths with files prefixed by a dot and suffixed with .d.tmp
+file2tmpdep = $(strip $(call dotfile,$(addsuffix .d.tmp,$(1))))
+
+# convert source/obj files into dot-cmd filename
+#  $1 list of paths
+#  return: full paths with files prefixed by a dot and suffixed with .cmd
+cmdfile = $(strip $(call dotfile,$(addsuffix .cmd,$(1))))
+
+# add a \ before each quote
+protect_quote = $(subst ','\'',$(1))
+#'# editor syntax highlight fix
+
+# return an non-empty string if $1 is empty, and vice versa
+#  $1 a string
+not = $(if $1,,true)
+
+# return 1 if parameter is a non-empty string, else 0
+boolean = $(if $1,1,0)
+
+# return an empty string if string are equal
+compare = $(strip $(subst $(1),,$(2)) $(subst $(2),,$(1)))
+
+# return a non-empty string if a file does not exist
+#  $1: file
+file_missing = $(call compare,$(wildcard $1),$1)
+
+# return a non-empty string if cmdline changed
+#  $1: file to be built
+#  $2: the command to build it
+cmdline_changed = $(call compare,$(strip $(cmd-$(1))),$(strip $(2)))
+
+# return an non-empty string if the .d file does not exist
+#  $1: the dep file (.d)
+depfile_missing = $(call compare,$(wildcard $(1)),$(1))
+
+# return a non-empty string if, according to dep-xyz variable, a file
+# needed to build $1 does not exist. In this case we need to rebuild
+# the file and the .d file.
+#  $1: file to be built
+dep-missing = $(call compare,$(wildcard $(dep-$(1))),$(dep-$(1)))
+
+# return an empty string if no prereq is newer than target
+#  $1: list of prerequisites newer than target ($?)
+dep-newer = $(strip $(filter-out FORCE,$(1)))
+
+# display why a file should be re-built
+#  $1: source files
+#  $2: dst file
+#  $3: build command
+#  $4: all prerequisites newer than target ($?)
+ifeq ($(D),1)
+display_deps = \
+       echo -n "$1 -> $2 " ; \
+       echo -n "file_missing=$(call boolean,$(call file_missing,$(2))) " ; \
+       echo -n "cmdline_changed=$(call boolean,$(call cmdline_changed,$(2),$(3))) " ; \
+       echo -n "depfile_missing=$(call boolean,$(call depfile_missing,$(call depfile,$(2)))) " ; \
+       echo -n "dep-missing=$(call boolean,$(call dep-missing,$(2))) " ; \
+       echo "dep-newer=$(call boolean,$(call dep-newer,$(4)))"
+else
+display_deps=
+endif
+
+# return an empty string if a file should be rebuilt
+#  $1: dst file
+#  $2: build command
+#  $3: all prerequisites newer than target ($?)
+check_deps = \
+       $(or $(call file_missing,$(1)),\
+       $(call cmdline_changed,$(1),$(2)),\
+       $(call depfile_missing,$(call depfile,$(1))),\
+       $(call dep-missing,$(1)),\
+       $(call dep-newer,$(3)))
+
+# create a depfile (.d) with no additional deps
+#  $1: object file (.o)
+create_empty_depfile = echo "dep-$(1) =" > $(call depfile,$(1))
+
+# save a command in a file
+#  $1: command to build the file
+#  $2: name of the file
+save_cmd = echo "cmd-$(2) = $(call protect_quote,$(1))" > $(call cmdfile,$(2))
+
+# remove the FORCE target from the list of all prerequisites $+
+#  no arguments, use $+
+prereq = $(filter-out FORCE,$(+))
diff --git a/mk/ucgine-vars.mk b/mk/ucgine-vars.mk
new file mode 100644 (file)
index 0000000..c07764e
--- /dev/null
@@ -0,0 +1,49 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+CROSS ?= $(ARCH_CROSS)
+
+# core tools
+CP ?= cp
+LN ?= ln
+GAWK ?= gawk
+GREP ?= grep
+# compiler and binutils, set_default overrides mk implicit value
+# but not command line or standard variables
+$(call set_default,CC,$(CROSS)gcc)
+$(call set_default,CPP,$(CROSS)cpp)
+$(call set_default,AR,$(CROSS)ar)
+$(call set_default,LD,$(CROSS)ld)
+$(call set_default,OBJCOPY,$(CROSS)objcopy)
+$(call set_default,OBJDUMP,$(CROSS)objdump)
+$(call set_default,STRIP,$(CROSS)strip)
+HOSTCC ?= cc
+
+CFLAGS += $(EXTRA_CFLAGS)
+CPPFLAGS += $(EXTRA_CPPFLAGS)
+LDFLAGS += $(EXTRA_LDFLAGS)
+LDLIBS += $(EXTRA_LDLIBS)
diff --git a/tools/cfzy/Makefile b/tools/cfzy/Makefile
new file mode 100644 (file)
index 0000000..4114bda
--- /dev/null
@@ -0,0 +1,71 @@
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+UCGINE ?= $(abspath ../..)
+
+# XXX we should not need UCGINE_ARCH
+UCGINE_ARCH = posix
+include $(UCGINE)/mk/ucgine-pre.mk
+
+O ?= $(CURDIR)/build
+
+CFLAGS  = -g -O2 -Wall
+CFLAGS += -Ilibconfizery
+
+src := libconfizery/cfzy_expr.c
+src += libconfizery/cfzy_list.c
+src += libconfizery/cfzy_htable.c
+src += libconfizery/cfzy_string.c
+src += libconfizery/cfzy_log.c
+src += libconfizery/cfzy_confnode.c
+src += libconfizery/cfzy_conftree.c
+src += libconfizery/cfzy_dotconfig.c
+src += libconfizery/cfzy_c_hdr.c
+src += libconfizery/cfzy_file.c
+src += libconfizery/cfzy_conftree_parser.c
+src += libconfizery/cfzy_confnode_choice.c
+src += libconfizery/cfzy_confnode_choiceconfig.c
+src += libconfizery/cfzy_confnode_comment.c
+src += libconfizery/cfzy_confnode_config.c
+src += libconfizery/cfzy_confnode_if.c
+src += libconfizery/cfzy_confnode_intconfig.c
+src += libconfizery/cfzy_confnode_menu.c
+src += libconfizery/cfzy_confnode_menuconfig.c
+src += libconfizery/cfzy_confnode_root.c
+src += libconfizery/cfzy_confnode_strconfig.c
+ar-y-$(O)/libconfizery/libconfizery.a := $(src)
+
+src := cfzy-basic/main.c
+src += $(O)/libconfizery/libconfizery.a
+exe-y-$(O)/cfzy-basic/cfzy-basic := $(src)
+
+include $(UCGINE)/mk/ucgine-post.mk
+
+.PHONY: all
+all: $(all-targets)
+
+.PHONY: clean
+clean: _ucgine_clean
diff --git a/tools/cfzy/build/cfzy-basic/cfzy-basic b/tools/cfzy/build/cfzy-basic/cfzy-basic
new file mode 100755 (executable)
index 0000000..9402548
Binary files /dev/null and b/tools/cfzy/build/cfzy-basic/cfzy-basic differ
diff --git a/tools/cfzy/cfzy-basic/Makefile b/tools/cfzy/cfzy-basic/Makefile
new file mode 100644 (file)
index 0000000..c3ac5f0
--- /dev/null
@@ -0,0 +1,11 @@
+include $(TOPDIR)/confizery.vars.mk
+
+PROG = cfzy-basic
+
+CFLAGS += -I$(SRCDIR)/libconfizery
+
+LDLIBS = $(BUILDDIR)/libconfizery/libconfizery.a
+
+SRCS  = main.c
+
+include $(TOPDIR)/confizery.prog.mk
diff --git a/tools/cfzy/cfzy-basic/main.c b/tools/cfzy/cfzy-basic/main.c
new file mode 100644 (file)
index 0000000..653ef40
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <cfzy_log.h>
+#include <cfzy_list.h>
+#include <cfzy_conftree.h>
+#include <cfzy_dotconfig.h>
+#include <cfzy_c_hdr.h>
+
+#define LOG(level, fmt, args...)                               \
+       CFZY_LOG("cfzy-basic", level, fmt, ##args)
+
+/* Parsed arguments */
+struct cfzy_arguments {
+       char *input_conftree;
+       char *input_dotconfig;
+       char *output_dotconfig;
+       char *output_c_hdr;
+       char *output_multi_c_hdr;
+};
+
+static void
+usage(const char *prog, int code)
+{
+       fprintf(stderr,
+               "\nUsage : \n");
+       fprintf(stderr,
+               "  %s [options]\n", prog);
+       fprintf(stderr,
+               "-h, --help\n"
+               "            show help\n");
+       fprintf(stderr,
+               "-d, --debug-level\n"
+               "            set debug level (0: no log, 4 noisy)\n");
+       fprintf(stderr,
+               "-c, --input-conftree\n"
+               "            path to input configuration tree file "
+               "(mandatory)\n");
+       fprintf(stderr,
+               "-i, --input-dotconfig\n"
+               "            path to input dotconfig file\n");
+       fprintf(stderr,
+               "-o, --output-dotconfig\n"
+               "            path to output dotconfig file\n");
+       fprintf(stderr,
+               "-O, --output-c-hdr\n"
+               "            path to output C header file\n");
+       fprintf(stderr,
+               "-m, --output-multi-c-hdr\n"
+               "            path to output multiple C header directory\n");
+       exit(code);
+}
+
+static void
+free_args(struct cfzy_arguments *cfzy_args)
+{
+       if (cfzy_args->input_conftree != NULL)
+               free(cfzy_args->input_conftree);
+       if (cfzy_args->input_dotconfig != NULL)
+               free(cfzy_args->input_dotconfig);
+       if (cfzy_args->output_dotconfig != NULL)
+               free(cfzy_args->output_dotconfig);
+       if (cfzy_args->output_c_hdr != NULL)
+               free(cfzy_args->output_c_hdr);
+       if (cfzy_args->output_multi_c_hdr != NULL)
+               free(cfzy_args->output_multi_c_hdr);
+}
+
+static void
+parse_args(struct cfzy_arguments *cfzy_args, int argc, char **argv)
+{
+       int ch;
+       const char * prog = argv[0];
+       int option_index;
+       int level;
+       static struct option lgopts[] = {
+               {"help", 0, 0, 'h'},
+               {"debug-level", 0, 0, 'd'},
+               {"input-conftree", 0, 0, 'c'},
+               {"input-dotconfig", 0, 0, 'i'},
+               {"output-dotconfig", 0, 0, 'o'},
+               {"output-c-hdr", 0, 0, 'O'},
+               {"output-multi-c-hdr", 0, 0, 'm'},
+               {NULL, 0, 0, 0}
+       };
+
+       while ((ch = getopt_long(argc, argv,
+                                "h"  /* help */
+                                "d:"  /* debug-level */
+                                "c:"  /* input-conftree */
+                                "i:"  /* input-dotconfig */
+                                "o:"  /* output-dotconfig */
+                                "O:"  /* output-c-hdr */
+                                "m:"  /* output-multi-c-hdr */
+                                ,
+                           lgopts, &option_index)) != -1) {
+               switch (ch) {
+               case 'h': /* help */
+                       free_args(cfzy_args);
+                       usage(prog, 0); /* will exit */
+                       break;
+
+               case 'd': /* debug-level */
+                       level = atoi(optarg);
+                       if (level < 0 || level > CFZY_LOG_DEBUG) {
+                               LOG(ERR, "invalid log level\n");
+                               free_args(cfzy_args);
+                               cfzy_log_exit();
+                               exit(1);
+                       }
+                       cfzy_log_set_default_level(level);
+                       break;
+
+               case 'c': /* input-conftree */
+                       cfzy_args->input_conftree = strdup(optarg);
+                       if (cfzy_args->input_conftree == NULL) {
+                               LOG(ERR, "Cannot alloc input conftree str\n");
+                               free_args(cfzy_args);
+                               cfzy_log_exit();
+                               exit(1);
+                       }
+                       break;
+
+               case 'i': /* input-dotconfig */
+                       cfzy_args->input_dotconfig = strdup(optarg);
+                       if (cfzy_args->input_dotconfig == NULL) {
+                               LOG(ERR, "Cannot alloc input dotconfig str\n");
+                               free_args(cfzy_args);
+                               cfzy_log_exit();
+                               exit(1);
+                       }
+                       break;
+
+               case 'o': /* output-dotconfig */
+                       cfzy_args->output_dotconfig = strdup(optarg);
+                       if (cfzy_args->output_dotconfig == NULL) {
+                               LOG(ERR, "Cannot alloc output dotconfig str\n");
+                               free_args(cfzy_args);
+                               cfzy_log_exit();
+                               exit(1);
+                       }
+                       break;
+
+               case 'O': /* output-c-hdr */
+                       cfzy_args->output_c_hdr = strdup(optarg);
+                       if (cfzy_args->output_c_hdr == NULL) {
+                               LOG(ERR, "Cannot alloc output c_hdr str\n");
+                               free_args(cfzy_args);
+                               cfzy_log_exit();
+                               exit(1);
+                       }
+                       break;
+
+               case 'm': /* output-multi-c-hdr */
+                       cfzy_args->output_multi_c_hdr = strdup(optarg);
+                       if (cfzy_args->output_multi_c_hdr == NULL) {
+                               LOG(ERR, "Cannot alloc output multi_c_hdr str\n");
+                               free_args(cfzy_args);
+                               cfzy_log_exit();
+                               exit(1);
+                       }
+                       break;
+
+               default:
+                       LOG(ERR, "invalid option\n");
+                       free_args(cfzy_args);
+                       cfzy_log_exit();
+                       usage(prog, 1); /* will exit */
+               }
+       }
+
+       /* conftree file is mandatory */
+       if (cfzy_args->input_conftree == NULL) {
+               LOG(ERR, "input conftree is mandatory\n");
+               free_args(cfzy_args);
+               cfzy_log_exit();
+               usage(prog, 1); /* will exit */
+       }
+
+       /* check that at least one output argument is given */
+       if (cfzy_args->output_dotconfig == NULL &&
+           cfzy_args->output_c_hdr == NULL &&
+           cfzy_args->output_multi_c_hdr == NULL) {
+               LOG(ERR, "at least one output argument is needed\n");
+               free_args(cfzy_args);
+               cfzy_log_exit();
+               usage(prog, 1); /* will exit */
+       }
+
+       argc -= optind;
+       argv += optind;
+       /* XXX parse mode */
+}
+
+/*
+ * configuration tree
+ *
+ * -i input .config:
+ *   default is none
+ *
+ * -o output .config
+ *   defaut is none
+ *
+ * ?? output autoconf.h
+ *
+ * output config/xxx.h
+ *
+ * -v verbose
+ *
+ * oldconfig:
+ *   prompt for options that have no user value
+ *
+ * silentoldconfig
+ *   set all options that have no user value to default
+ *
+ * config
+ *    ?
+ *
+ * defconfig
+ *    ?
+ *
+ * randconfig
+ *    set all options to a random value (only for booleans, other
+ *    values are set to default)
+ *
+ * allyesconfig
+ *    set all options to y (booleans only)
+ *
+ * allnoconfig
+ *    set all options to n (booleans only)
+ *
+ * - ouvrir le fichier conftree
+ * - si -i, ouvrir .config
+ * - effectue les operations
+ * - si -o et/ou ... output
+ */
+
+static int
+do_configuration(struct cfzy_arguments *cfzy_args)
+{
+       struct cfzy_confnode *n;
+       struct cfzy_conftree *conftree;
+       char buf[BUFSIZ];
+       char *s;
+       int ret;
+
+       /* parse configuration tree */
+       conftree = cfzy_conftree_new(cfzy_args->input_conftree);
+       if (conftree == NULL) {
+               LOG(ERR, "Cannot parse configuration tree\n");
+               return -1;
+       }
+
+       /* parse .config if provided */
+       if (cfzy_args->input_dotconfig != NULL &&
+           cfzy_dotconfig_read(conftree, cfzy_args->input_dotconfig) < 0) {
+               cfzy_conftree_free(conftree);
+               LOG(ERR, "Cannot parse dotconfig <%s>\n",
+                   cfzy_args->input_dotconfig);
+               return -1;
+       }
+
+#if 0
+       /* evaluate effective values of conftree */
+       if (cfzy_conftree_evaluate(conftree) < 0) {
+               cfzy_conftree_free(conftree);
+               LOG(ERR, "Cannot evaluate configuration tree\n");
+               return -1;
+       }
+#endif
+
+       memset(buf, 0, sizeof(buf));
+
+       /* browse all nodes following priority list and prompt user */
+       TAILQ_FOREACH(n, &conftree->prio_list, prio_next) {
+
+               if (n->flags & CFZY_F_INVISIBLE)
+                       continue;
+               if (n->flags & CFZY_F_NO_NAME)
+                       continue;
+
+               /* node has a user value or user value cannot be set,
+                * just display it */
+               if ((n->user_value != NULL) || (n->flags & CFZY_F_NO_SET_VALUE)) {
+                       /* XXX we should have a difference between
+                        * NO_VALUE (menu for instance) and
+                        * NO_SET_VALUE */
+                       cfzy_confnode_evaluate(n);
+                       printf("%s=%s\n", cfzy_confnode_name(n),
+                              n->effective_value);
+                       continue;
+               }
+
+               /* node cannot be enabled due to deps */
+               ret = cfzy_confnode_check_deps(n);
+               if (ret == 0) {
+                       cfzy_confnode_evaluate(n);
+                       printf("%s=%s\n", cfzy_confnode_name(n),
+                              n->effective_value);
+                       continue;
+               }
+               if (ret < 0) {
+                       LOG(ERR, "Cannot check deps\n");
+                       return -1;
+               }
+
+               /* ok, now prompt user */
+
+               cfzy_confnode_dump(n, stdout, CFZY_DUMP_MODE_STANDARD);
+
+               while (1) {
+                       printf("Enter user value for %s > ",
+                              cfzy_confnode_name(n));
+                       fflush(stdout);
+
+                       fgets(buf, sizeof(buf) - 1, stdin);
+                       s = buf;
+                       strsep(&s, "\r\n");
+
+                       /* empty buf means default value */
+                       if (strcmp(buf, "") == 0)
+                               break;
+
+                       /* invalid value, prompt again */
+                       if (cfzy_confnode_set_uservalue(n, buf) < 0) {
+                               printf("invalid value\n");
+                               continue;
+                       }
+
+                       break;
+               }
+
+               cfzy_confnode_evaluate(n);
+               printf("%s=%s\n", cfzy_confnode_name(n),
+                      n->effective_value);
+       }
+
+       /* output .config if provided */
+       if (cfzy_args->output_dotconfig != NULL &&
+           cfzy_dotconfig_write(conftree, cfzy_args->output_dotconfig) < 0) {
+               cfzy_conftree_free(conftree);
+               LOG(ERR, "Cannot output dotconfig <%s>\n",
+                   cfzy_args->output_dotconfig);
+               return -1;
+       }
+
+       /* output C header if provided */
+       if (cfzy_args->output_c_hdr != NULL &&
+           cfzy_c_hdr_write(conftree, cfzy_args->output_c_hdr) < 0) {
+               cfzy_conftree_free(conftree);
+               LOG(ERR, "Cannot output C header <%s>\n",
+                   cfzy_args->output_c_hdr);
+               return -1;
+       }
+
+       /* output multiple C headers if provided */
+       if (cfzy_args->output_multi_c_hdr != NULL &&
+           cfzy_multi_c_hdr_write(conftree, cfzy_args->output_multi_c_hdr) < 0) {
+               cfzy_conftree_free(conftree);
+               LOG(ERR, "Cannot output multiple C headers <%s>\n",
+                   cfzy_args->output_multi_c_hdr);
+               return -1;
+       }
+
+       cfzy_conftree_free(conftree);
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       int ret = EXIT_SUCCESS;
+       struct cfzy_arguments cfzy_args;
+
+       /* XXX a cfzy_init() would be better */
+       cfzy_log_init();
+
+       memset(&cfzy_args, 0, sizeof(cfzy_args));
+
+       /* argument parsing is always successful (will exit on error) */
+       parse_args(&cfzy_args, argc, argv);
+
+       /* do the configuration */
+       if (do_configuration(&cfzy_args) < 0) {
+               LOG(ERR, "Configuration failed");
+               ret = EXIT_FAILURE;
+       }
+
+       free_args(&cfzy_args);
+       cfzy_log_exit();
+
+       return ret;
+}
diff --git a/tools/cfzy/cfzy-test/Makefile b/tools/cfzy/cfzy-test/Makefile
new file mode 100644 (file)
index 0000000..691a0f5
--- /dev/null
@@ -0,0 +1,14 @@
+include $(TOPDIR)/confizery.vars.mk
+
+PROG = confizery-test
+
+CFLAGS += -I$(SRCDIR)/libconfizery
+
+LDLIBS = $(BUILDDIR)/libconfizery/libconfizery.a
+
+SRCS  = main.c
+SRCS += test_expr.c
+SRCS += test_conftree.c
+SRCS += test_dotconfig.c
+
+include $(TOPDIR)/confizery.prog.mk
diff --git a/tools/cfzy/cfzy-test/invalid-configs/circular-dep.cfzy b/tools/cfzy/cfzy-test/invalid-configs/circular-dep.cfzy
new file mode 100644 (file)
index 0000000..1d8af7f
--- /dev/null
@@ -0,0 +1,8 @@
+# circular dependency between FOO and BAR
+config FOO
+       prompt "hello"
+       default y
+       requires BAR
+
+config BAR
+       default y if FOO
diff --git a/tools/cfzy/cfzy-test/invalid-configs/circular-dep2.cfzy b/tools/cfzy/cfzy-test/invalid-configs/circular-dep2.cfzy
new file mode 100644 (file)
index 0000000..27fc451
--- /dev/null
@@ -0,0 +1,11 @@
+# circular dependency between FOO, BAR and TOTO
+config FOO
+       prompt "hello"
+       default y
+       requires TOTO
+
+menuconfig BAR
+       default y
+       requires FOO
+
+config TOTO
diff --git a/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val b/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val
new file mode 100644 (file)
index 0000000..17804c7
--- /dev/null
@@ -0,0 +1,17 @@
+#
+# -- Menu 1
+#
+CONFIG_MENU1_CONFIG1=y
+# Hello
+# CONFIG_MENU1_CONFIG2 is not set
+# CONFIG_MENU1_CONFIG3 is not set
+CONFIG_MENU1_CHOICE="MENU1_CHOICE2"
+# CONFIG_MENU1_CHOICE1 is not set
+CONFIG_MENU1_CHOICE2=y
+# CONFIG_MENU1_CHOICE3 is not set
+CONFIG_INT_EXAMPLE=a
+CONFIG_STR_EXAMPLE="my default value"
+#
+# -- My menuconfig node
+#
+# CONFIG_MENUCONFIG1 is not set
diff --git a/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val2 b/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val2
new file mode 100644 (file)
index 0000000..a1ed3fb
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# -- Menu 1
+#
+CONFIG_MENU1_CONFIG1=y
+# Hello
+# CONFIG_MENU1_CONFIG2 is not set
+# CONFIG_MENU1_CONFIG3 is not set
+CONFIG_MENU1_CHOICE="MENU1_CHOICE2"
+# CONFIG_MENU1_CHOICE1 is not set
+CONFIG_MENU1_CHOICE2=y d
+# CONFIG_MENU1_CHOICE3 is not set
+CONFIG_STR_EXAMPLE="my default value"
+#
+# -- My menuconfig node
+#
+# CONFIG_MENUCONFIG1 is not set
diff --git a/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val3 b/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val3
new file mode 100644 (file)
index 0000000..c7e08fc
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# -- Menu 1
+#
+z
+CONFIG_MENU1_CONFIG1=y
+# Hello
+# CONFIG_MENU1_CONFIG2 is not set
+# CONFIG_MENU1_CONFIG3 is not set
+CONFIG_MENU1_CHOICE="MENU1_CHOICE2"
+# CONFIG_MENU1_CHOICE1 is not set
+# CONFIG_MENU1_CHOICE3 is not set
+CONFIG_STR_EXAMPLE="my default value"
+#
+# -- My menuconfig node
+#
+# CONFIG_MENUCONFIG1 is not set
diff --git a/tools/cfzy/cfzy-test/invalid-configs/dup-name.cfzy b/tools/cfzy/cfzy-test/invalid-configs/dup-name.cfzy
new file mode 100644 (file)
index 0000000..6bc2c4d
--- /dev/null
@@ -0,0 +1,4 @@
+# duplicate config name
+config FOO
+
+intconfig FOO
diff --git a/tools/cfzy/cfzy-test/invalid-configs/invalid-attr.cfzy b/tools/cfzy/cfzy-test/invalid-configs/invalid-attr.cfzy
new file mode 100644 (file)
index 0000000..7ed2e4b
--- /dev/null
@@ -0,0 +1,4 @@
+# invalid attribute
+config FOO
+       prompt "hello"
+       foo bar
diff --git a/tools/cfzy/cfzy-test/invalid-configs/invalid-command.cfzy b/tools/cfzy/cfzy-test/invalid-configs/invalid-command.cfzy
new file mode 100644 (file)
index 0000000..adb927e
--- /dev/null
@@ -0,0 +1,3 @@
+# invalid command
+foo bar
+       prompt "hello"
diff --git a/tools/cfzy/cfzy-test/invalid-configs/invalid-expr.cfzy b/tools/cfzy/cfzy-test/invalid-configs/invalid-expr.cfzy
new file mode 100644 (file)
index 0000000..6bf8fad
--- /dev/null
@@ -0,0 +1,4 @@
+# invalid expression
+config FOO
+       prompt "hello"
+       default y if ( invalid expression xx
diff --git a/tools/cfzy/cfzy-test/invalid-configs/invalid-value.cfzy b/tools/cfzy/cfzy-test/invalid-configs/invalid-value.cfzy
new file mode 100644 (file)
index 0000000..20a4ff5
--- /dev/null
@@ -0,0 +1,4 @@
+# invalid default value
+config FOO
+       prompt "hello"
+       default bar
diff --git a/tools/cfzy/cfzy-test/invalid-configs/node-not-closed.cfzy b/tools/cfzy/cfzy-test/invalid-configs/node-not-closed.cfzy
new file mode 100644 (file)
index 0000000..53c5d4c
--- /dev/null
@@ -0,0 +1,6 @@
+# menuconfig node not closed
+menuconfig FOO
+
+config BAR
+
+# missing endmenuconfig here
diff --git a/tools/cfzy/cfzy-test/main.c b/tools/cfzy/cfzy-test/main.c
new file mode 100644 (file)
index 0000000..9a49c27
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#include <cfzy_log.h>
+
+#include "test_expr.h"
+#include "test_conftree.h"
+#include "test_dotconfig.h"
+
+int main(int argc, char **argv)
+{
+       int ret = 0;
+
+       (void)argc;
+
+       /* XXX a cfzy_init() would be better */
+       cfzy_log_init();
+
+       printf("=== test expression parser ===\n");
+       if (test_expr() < 0) {
+               printf("failed\n");
+               ret = 1;
+       }
+       else
+               printf("success\n");
+
+       printf("=== test conf tree ===\n");
+       if (test_conftree(argv[0]) < 0) {
+               printf("failed\n");
+               ret = 1;
+       }
+       else
+               printf("success\n");
+
+       printf("=== test .config ===\n");
+       if (test_dotconfig(argv[0]) < 0) {
+               printf("failed\n");
+               ret = 1;
+       }
+       else
+               printf("success\n");
+
+       cfzy_log_exit();
+
+       if (ret == 0)
+               printf("\n=== SUCCES ===\n");
+       else
+               printf("\n=== FAILED ===\n");
+
+       return ret;
+}
diff --git a/tools/cfzy/cfzy-test/test-configs/conftree.cfzy b/tools/cfzy/cfzy-test/test-configs/conftree.cfzy
new file mode 100644 (file)
index 0000000..3f65270
--- /dev/null
@@ -0,0 +1,98 @@
+#
+# comments are prefixed by #
+#
+
+# a menu node is a node that has no value but contains several
+# other nodes
+menu MENU1
+       prompt "Menu 1"
+       # comments can also be added inside a node
+
+# a config node is the most basic node, storing a boolean value
+config MENU1_CONFIG1
+       prompt "A config example"
+       default y
+
+# a comment node is a node that has no value but it will display
+# a prompt in the GUI
+comment "Hello"
+
+# a node can have several default values: each expression is evaluated
+# in the same order until one matches. If no expression matches, the
+# default value of the node is used
+config MENU1_CONFIG2
+       prompt "Another config example"
+       default y if !MENU1_CONFIG1
+       default n
+
+# environment variables can be used anywhere in a conftree file: they
+# are evaluated and replaced by their value before parsing the file.
+config MENU1_CONFIG3
+       prompt "A config example"
+       ---help---
+       The content of the PATH variable is $(PATH)
+
+# a choice node cntains several choiceconfig nodes that are exclusive
+# each other
+choice MENU1_CHOICE
+       prompt "This is menu 1 choice 1"
+       default MENU1_CHOICE2 if MENU1_CONFIG1
+       default MENU1_CHOICE3
+       ---help---
+       help of menu1_choice: this is a choice between
+       several values.
+
+choiceconfig MENU1_CHOICE1
+       prompt "choice 1"
+
+choiceconfig MENU1_CHOICE2
+       prompt "choice 2"
+
+choiceconfig MENU1_CHOICE3
+       prompt "choice 3"
+
+endchoice # this closes the "choice" node
+
+# an intconfig node stores an integer value
+intconfig INT_EXAMPLE
+       prompt "integer example"
+       default 12000000
+       ---help---
+       This is the help of the integer node
+
+# a strconfig stores a string
+strconfig STR_EXAMPLE
+       prompt "A strconfig example"
+       default "my default value"
+
+endmenu
+
+# A menuconfig node is a menu node that can be enabled or disabled.
+# The children nodes are available only if the node is enabled.
+menuconfig MENUCONFIG1
+       prompt "My menuconfig node"
+       # If a line is too long, it can be splitted with a backslash
+       default y if !MENU1_CONFIG1 && \
+               MENU1_CHOICE1
+       ---help---
+       Help for the menuconfig node
+
+# the "requires" attribute sets a list of expressions that must be
+# evaluated to True to enable the node.
+config MENUCONFIG1_CONFIG1
+       prompt "again, a config"
+       requires MENU1_CONFIG1
+       requires !MENU1_CHOICE3
+
+if MENU1_CHOICE1 || MENU1_CHOICE2
+
+config MENUCONFIG1_CONFIG2
+       prompt "still another config"
+
+# source another sub conftree file: the path can be relative to this file
+# or absolute. The "./" is not mandatory here.
+source "./subconftree.cfzy"
+
+endif
+
+endmenuconfig
diff --git a/tools/cfzy/cfzy-test/test-configs/dotconfig b/tools/cfzy/cfzy-test/test-configs/dotconfig
new file mode 100644 (file)
index 0000000..190e54b
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# -- Menu 1
+#
+CONFIG_MENU1_CONFIG1=y
+
+# unknown node name (should be successful anyway)
+CONFIG_MENU1_CONFIG1XAZXZXZ=y
+
+# Hello
+# CONFIG_MENU1_CONFIG2 is not set
+# CONFIG_MENU1_CONFIG3 is not set
+CONFIG_MENU1_CHOICE="MENU1_CHOICE2"
+# CONFIG_MENU1_CHOICE1 is not set
+CONFIG_MENU1_CHOICE2=y
+# CONFIG_MENU1_CHOICE3 is not set
+CONFIG_STR_EXAMPLE="my default value"
+#
+# -- My menuconfig node
+#
+# CONFIG_MENUCONFIG1 is not set
+
+# same option several times (should be ok too)
+CONFIG_INT_EXAMPLE=1234
+CONFIG_INT_EXAMPLE=1234
+CONFIG_INT_EXAMPLE=1234
diff --git a/tools/cfzy/cfzy-test/test-configs/subconftree.cfzy b/tools/cfzy/cfzy-test/test-configs/subconftree.cfzy
new file mode 100644 (file)
index 0000000..2cc99de
--- /dev/null
@@ -0,0 +1,2 @@
+config FOO
+       prompt "yet another boolean config"
diff --git a/tools/cfzy/cfzy-test/test_conftree.c b/tools/cfzy/cfzy-test/test_conftree.c
new file mode 100644 (file)
index 0000000..3bf3d27
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <libgen.h>
+
+#include <cfzy_log.h>
+#include <cfzy_list.h>
+#include <cfzy_htable.h>
+#include <cfzy_confnode.h>
+#include <cfzy_conftree.h>
+#include <cfzy_dotconfig.h>
+#include <cfzy_c_hdr.h>
+
+#include "test_conftree.h"
+
+#define LOG(level, fmt, args...)                               \
+       CFZY_LOG("test_conftree", level, fmt, ##args)
+
+#define SUCCESS                      0
+#define CANNOT_PARSE_CONFTREE       -1
+#define CANNOT_DUMP_CONFTREE        -2
+#define CANNOT_FIND_SYMBOL          -3
+#define CANNOT_EVAL_CONFTREE        -4
+#define CANNOT_GET_VALUE            -5
+#define CANNOT_SET_VALUE            -6
+#define INVALID_VALUE               -7
+#define CANNOT_FILTER_CONFTREE      -8
+#define CANNOT_SAVE_CONFTREE_VALUES -9
+
+/* check that a node is set to the expected value */
+static int test_one_confnode(const struct cfzy_conftree *conftree,
+                            const char *name, int expected_value)
+{
+       int val;
+
+       val = cfzy_confnode_get_boolvalue(name, conftree);
+       if (val < 0) {
+               LOG(DEBUG, "cannot get node value\n");
+               return CANNOT_GET_VALUE;
+       }
+       if (val != expected_value) {
+               LOG(DEBUG, "invalid val for %s: val=%d, expect=%d\n",
+                       name, val, expected_value);
+               return INVALID_VALUE;
+       }
+
+       return SUCCESS;
+}
+
+static int test_one_conftree(const char *name)
+{
+       struct cfzy_list_head *node_list = NULL;
+       struct cfzy_list_elt *e;
+       struct cfzy_conftree *conftree;
+       struct cfzy_confnode *n;
+       int ret;
+
+       conftree = cfzy_conftree_new(name);
+       if (conftree == NULL)
+               return CANNOT_PARSE_CONFTREE;
+
+       if (cfzy_conftree_evaluate(conftree) < 0) {
+               LOG(DEBUG, "cannot eval conftree\n");
+               cfzy_conftree_free(conftree);
+               return CANNOT_EVAL_CONFTREE;
+       }
+
+       if (cfzy_conftree_dump(conftree, "/tmp/conftree.dump") < 0) {
+               cfzy_conftree_free(conftree);
+               return CANNOT_DUMP_CONFTREE;
+       }
+
+       /* node value should be 1 (default) */
+       ret = test_one_confnode(conftree, "MENU1_CONFIG1", 1);
+       if (ret < 0) {
+               cfzy_conftree_free(conftree);
+               return ret;
+       }
+
+       /* node value should be 0 (default) */
+       ret = test_one_confnode(conftree, "MENU1_CONFIG2", 0);
+       if (ret < 0) {
+               cfzy_conftree_free(conftree);
+               return ret;
+       }
+
+       /* lookup for this node */
+       n = cfzy_htable_lookup(conftree->nodes_htable, "MENU1_CONFIG1");
+       if (n == NULL) {
+               LOG(DEBUG, "cannot find node\n");
+               cfzy_conftree_free(conftree);
+               return CANNOT_FIND_SYMBOL;
+       }
+
+       /* try to assign an invalid value: should fail */
+       if (cfzy_confnode_set_uservalue(n, "dummy") == 0) {
+               LOG(DEBUG, "set_uservalue returned 0 but should not\n");
+               cfzy_conftree_free(conftree);
+               return CANNOT_SET_VALUE;
+       }
+
+       /* set thiis node to NO */
+       if (cfzy_confnode_set_uservalue(n, "n") < 0) {
+               LOG(DEBUG, "cannot set node value\n");
+               cfzy_conftree_free(conftree);
+               return CANNOT_SET_VALUE;
+       }
+
+       /* node value should be 1 because we did not evaluate the tree */
+       ret = test_one_confnode(conftree, "MENU1_CONFIG1", 1);
+       if (ret < 0) {
+               cfzy_conftree_free(conftree);
+               return ret;
+       }
+
+       if (cfzy_conftree_evaluate(conftree) < 0) {
+               LOG(DEBUG, "cannot eval conftree\n");
+               cfzy_conftree_free(conftree);
+               return CANNOT_EVAL_CONFTREE;
+       }
+
+       if (cfzy_conftree_dump(conftree, "/tmp/conftree2.dump") < 0) {
+               cfzy_conftree_free(conftree);
+               return CANNOT_DUMP_CONFTREE;
+       }
+
+       if (cfzy_conftree_save_values(conftree) < 0) {
+               cfzy_conftree_free(conftree);
+               return CANNOT_SAVE_CONFTREE_VALUES;
+       }
+
+       if (cfzy_conftree_dump(conftree, "/tmp/conftree3.dump") < 0) {
+               cfzy_conftree_free(conftree);
+               return CANNOT_DUMP_CONFTREE;
+       }
+
+       /* node value should now be 0 */
+       ret = test_one_confnode(conftree, "MENU1_CONFIG1", 0);
+       if (ret < 0) {
+               cfzy_conftree_free(conftree);
+               return ret;
+       }
+
+       /* node value should now be 1 (depends on !MENU1_CONFIG1) */
+       ret = test_one_confnode(conftree, "MENU1_CONFIG2", 1);
+       if (ret < 0) {
+               cfzy_conftree_free(conftree);
+               return ret;
+       }
+
+       /* test "choice" nodes */
+
+       /* node value should be 0 (default) */
+       ret = test_one_confnode(conftree, "MENU1_CHOICE1", 0);
+       if (ret < 0) {
+               cfzy_conftree_free(conftree);
+               return ret;
+       }
+
+       /* node value should be 0 (default) */
+       ret = test_one_confnode(conftree, "MENU1_CHOICE2", 0);
+       if (ret < 0) {
+               cfzy_conftree_free(conftree);
+               return ret;
+       }
+
+       /* node value should be 1 (default) */
+       ret = test_one_confnode(conftree, "MENU1_CHOICE3", 1);
+       if (ret < 0) {
+               cfzy_conftree_free(conftree);
+               return ret;
+       }
+
+       /* lookup for this node */
+       n = cfzy_htable_lookup(conftree->nodes_htable, "MENU1_CHOICE1");
+       if (n == NULL) {
+               LOG(DEBUG, "cannot find node\n");
+               cfzy_conftree_free(conftree);
+               return CANNOT_FIND_SYMBOL;
+       }
+
+       /* set this node to Y */
+       if (cfzy_confnode_set_uservalue(n, "y") < 0) {
+               LOG(DEBUG, "cannot set node value\n");
+               cfzy_conftree_free(conftree);
+               return CANNOT_SET_VALUE;
+       }
+
+       if (cfzy_conftree_evaluate(conftree) < 0) {
+               LOG(DEBUG, "cannot eval conftree\n");
+               cfzy_conftree_free(conftree);
+               return CANNOT_EVAL_CONFTREE;
+       }
+
+       if (cfzy_conftree_dump(conftree, "/tmp/conftree.dump") < 0) {
+               cfzy_conftree_free(conftree);
+               return CANNOT_DUMP_CONFTREE;
+       }
+
+       /* node value should be 1 */
+       ret = test_one_confnode(conftree, "MENU1_CHOICE1", 1);
+       if (ret < 0) {
+               cfzy_conftree_free(conftree);
+               return ret;
+       }
+
+       /* node value should be 0 */
+       ret = test_one_confnode(conftree, "MENU1_CHOICE2", 0);
+       if (ret < 0) {
+               cfzy_conftree_free(conftree);
+               return ret;
+       }
+
+       /* node value should be 0 */
+       ret = test_one_confnode(conftree, "MENU1_CHOICE3", 0);
+       if (ret < 0) {
+               cfzy_conftree_free(conftree);
+               return ret;
+       }
+
+       /* check the "menuconfig" node value, should be 1 (default) */
+       ret = test_one_confnode(conftree, "MENUCONFIG1", 1);
+       if (ret < 0) {
+               cfzy_conftree_free(conftree);
+               return ret;
+       }
+
+       /* check which node were changed by the user */
+       node_list = cfzy_conftree_filter(conftree, CFZY_FILTER_F_USR_CHANGED);
+       if (node_list == NULL) {
+               cfzy_conftree_free(conftree);
+               return CANNOT_FILTER_CONFTREE;
+       }
+       TAILQ_FOREACH(e, node_list, next) {
+               n = e->ptr;
+               printf("node %s\n", cfzy_confnode_name(n));
+       }
+       cfzy_list_free(node_list, NULL);
+
+       /* check which effective value changed */
+       node_list = cfzy_conftree_filter(conftree, CFZY_FILTER_F_EFF_CHANGED);
+       if (node_list == NULL) {
+               cfzy_conftree_free(conftree);
+               return CANNOT_FILTER_CONFTREE;
+       }
+       TAILQ_FOREACH(e, node_list, next) {
+               n = e->ptr;
+               printf("node2 %s\n", cfzy_confnode_name(n));
+       }
+       cfzy_list_free(node_list, NULL);
+
+       cfzy_conftree_free(conftree);
+       return 0;
+}
+
+static int test_one_invalid_conftree(const char *name)
+{
+       struct cfzy_conftree *conftree;
+
+       conftree = cfzy_conftree_new(name);
+       if (conftree == NULL)
+               return CANNOT_PARSE_CONFTREE;
+
+       cfzy_conftree_free(conftree);
+       return 0;
+}
+
+static void print_error(const char *in, int err)
+{
+       printf("Unexpected return value when parsing: <%s>\n", in);
+       switch (err) {
+               case SUCCESS:
+                       printf("Test returned success, but should not\n");
+                       break;
+               case CANNOT_PARSE_CONFTREE:
+                       printf("Cannot parse configuration tree\n");
+                       break;
+               case CANNOT_DUMP_CONFTREE:
+                       printf("Cannot dump configuration tree\n");
+                       break;
+               case CANNOT_EVAL_CONFTREE:
+                       printf("Cannot evalulate configuration tree\n");
+                       break;
+               case CANNOT_FIND_SYMBOL:
+                       printf("Cannot find symbol\n");
+                       break;
+               case CANNOT_GET_VALUE:
+                       printf("Cannot get node value\n");
+                       break;
+               case CANNOT_SET_VALUE:
+                       printf("Cannot set node value\n");
+                       break;
+               case INVALID_VALUE:
+                       printf("invalid value\n");
+                       break;
+               case CANNOT_FILTER_CONFTREE:
+                       printf("Cannot filter configuration tree\n");
+                       break;
+               case CANNOT_SAVE_CONFTREE_VALUES:
+                       printf("Cannot save configuration tree values\n");
+                       break;
+               default:
+                       printf("Invalid error %d\n", err);
+                       break;
+       }
+}
+
+int test_conftree(const char *progname)
+{
+       char filename[PATH_MAX];
+       char *progdir;
+       int err;
+
+       cfzy_log_set_default_level(CFZY_LOG_DEBUG);
+
+       /* progdir is the directory where confizery-test is located */
+       progdir = strdup(progname);
+       dirname(progdir);
+
+       /* unexistant file */
+       snprintf(filename, sizeof(filename), "foobar");
+       err = test_one_invalid_conftree(filename);
+       if (err != CANNOT_PARSE_CONFTREE) {
+               print_error(filename, err);
+               goto fail;
+       }
+
+       /* invalid command */
+       snprintf(filename, sizeof(filename),
+                "%s/../../src/confizery-test/invalid-configs/"
+                "invalid-command.cfzy", progdir);
+       err = test_one_invalid_conftree(filename);
+       if (err != CANNOT_PARSE_CONFTREE) {
+               print_error(filename, err);
+               goto fail;
+       }
+
+       /* invalid attribute */
+       snprintf(filename, sizeof(filename),
+                "%s/../../src/confizery-test/invalid-configs/"
+                "invalid-attribute.cfzy", progdir);
+       err = test_one_invalid_conftree(filename);
+       if (err != CANNOT_PARSE_CONFTREE) {
+               print_error(filename, err);
+               goto fail;
+       }
+
+       /* circular dependency */
+       snprintf(filename, sizeof(filename),
+                "%s/../../src/confizery-test/invalid-configs/"
+                "circular-dep.cfzy", progdir);
+       err = test_one_invalid_conftree(filename);
+       if (err != CANNOT_PARSE_CONFTREE) {
+               print_error(filename, err);
+               goto fail;
+       }
+
+       /* circular dependency */
+       snprintf(filename, sizeof(filename),
+                "%s/../../src/confizery-test/invalid-configs/"
+                "circular-dep2.cfzy", progdir);
+       err = test_one_invalid_conftree(filename);
+       if (err != CANNOT_PARSE_CONFTREE) {
+               print_error(filename, err);
+               goto fail;
+       }
+
+       /* node not closed */
+       snprintf(filename, sizeof(filename),
+                "%s/../../src/confizery-test/invalid-configs/"
+                "node-not-closed.cfzy", progdir);
+       err = test_one_invalid_conftree(filename);
+       if (err != CANNOT_PARSE_CONFTREE) {
+               print_error(filename, err);
+               goto fail;
+       }
+
+       /* duplicated config name */
+       snprintf(filename, sizeof(filename),
+                "%s/../../src/confizery-test/invalid-configs/"
+                "dup-name.cfzy", progdir);
+       err = test_one_invalid_conftree(filename);
+       if (err != CANNOT_PARSE_CONFTREE) {
+               print_error(filename, err);
+               goto fail;
+       }
+
+       /* invalid default value */
+       snprintf(filename, sizeof(filename),
+                "%s/../../src/confizery-test/invalid-configs/"
+                "invalid-value.cfzy", progdir);
+       err = test_one_invalid_conftree(filename);
+       if (err != CANNOT_PARSE_CONFTREE) {
+               print_error(filename, err);
+               goto fail;
+       }
+
+       /* invalid expression */
+       snprintf(filename, sizeof(filename),
+                "%s/../../src/confizery-test/invalid-configs/"
+                "invalid-expr.cfzy", progdir);
+       err = test_one_invalid_conftree(filename);
+       if (err != CANNOT_PARSE_CONFTREE) {
+               print_error(filename, err);
+               goto fail;
+       }
+
+       /* valid config */
+
+       /* an example config */
+       snprintf(filename, sizeof(filename),
+                "%s/../../src/confizery-test/test-configs/"
+                "conftree.cfzy", progdir);
+       err = test_one_conftree(filename);
+       if (err != SUCCESS) {
+               print_error(filename, err);
+               goto fail;
+       }
+
+       free(progdir);
+       return 0;
+
+ fail:
+       free(progdir);
+       return -1;
+}
diff --git a/tools/cfzy/cfzy-test/test_conftree.h b/tools/cfzy/cfzy-test/test_conftree.h
new file mode 100644 (file)
index 0000000..2445af6
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TEST_CONFTREE_H_
+#define _TEST_CONFTREE_H_
+
+/* test cfzy_conftree module */
+int test_conftree(const char *progname);
+
+#endif
diff --git a/tools/cfzy/cfzy-test/test_dotconfig.c b/tools/cfzy/cfzy-test/test_dotconfig.c
new file mode 100644 (file)
index 0000000..edf57f6
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <libgen.h>
+
+#include <cfzy_log.h>
+#include <cfzy_list.h>
+#include <cfzy_htable.h>
+#include <cfzy_confnode.h>
+#include <cfzy_conftree.h>
+#include <cfzy_dotconfig.h>
+#include <cfzy_c_hdr.h>
+
+#include "test_dotconfig.h"
+
+#define LOG(level, fmt, args...)                               \
+       CFZY_LOG("test_dotconfig", level, fmt, ##args)
+
+#define SUCCESS                      0
+#define CANNOT_PARSE_CONFTREE       -1
+#define CANNOT_EVAL_CONFTREE        -2
+#define CANNOT_GENERATE_DOTCONFIG   -3
+#define CANNOT_GENERATE_C_HDR       -4
+#define CANNOT_READ_DOTCONFIG       -5
+
+static void print_error(const char *in, int err)
+{
+       printf("Unexpected return value when parsing: <%s>\n", in);
+       switch (err) {
+               case SUCCESS:
+                       printf("Test returned success, but should not\n");
+                       break;
+               case CANNOT_PARSE_CONFTREE:
+                       printf("Cannot parse configuration tree\n");
+                       break;
+               case CANNOT_EVAL_CONFTREE:
+                       printf("Cannot evalulate configuration tree\n");
+                       break;
+               case CANNOT_GENERATE_DOTCONFIG:
+                       printf("Cannot generate dotconfig file\n");
+                       break;
+               case CANNOT_GENERATE_C_HDR:
+                       printf("Cannot generate C header file\n");
+                       break;
+               case CANNOT_READ_DOTCONFIG:
+                       printf("Cannot read dotconfig file\n");
+                       break;
+               default:
+                       printf("Invalid error %d\n", err);
+                       break;
+       }
+}
+
+static int test_one_dotconfig(struct cfzy_conftree *conftree,
+                             const char *filename, int expected)
+{
+       int ret, err;
+
+       printf("-- Test dotconfig <%s>\n", filename);
+
+       /* XXX we should reset config */
+
+       ret = cfzy_dotconfig_read(conftree, filename);
+       if (ret < 0)
+               err = CANNOT_READ_DOTCONFIG;
+       else
+               err = SUCCESS;
+
+       /* this is the expected result, return success */
+       if (err == expected)
+               return 0;
+
+       print_error(filename, err);
+       return -1;
+}
+
+int test_dotconfig(const char *progname)
+{
+       struct cfzy_conftree *conftree = NULL;
+       int ret;
+       char filename[PATH_MAX];
+       char *progdir;
+
+       cfzy_log_set_default_level(CFZY_LOG_DEBUG);
+
+       /* progdir is the directory where confizery-test is located */
+       progdir = strdup(progname);
+       dirname(progdir);
+
+       /* an example config */
+       snprintf(filename, sizeof(filename),
+                "%s/../../src/confizery-test/test-configs/"
+                "conftree.cfzy", progdir);
+
+       conftree = cfzy_conftree_new(filename);
+       if (conftree == NULL) {
+               LOG(DEBUG, "cannot parse configuration tree\n");
+               goto fail;
+       }
+
+       if (cfzy_conftree_evaluate(conftree) < 0) {
+               LOG(DEBUG, "cannot eval conftree\n");
+               goto fail;
+       }
+
+       /* write the dotconfig file */
+       ret = cfzy_dotconfig_write(conftree, "/tmp/dotconfig");
+       if (ret < 0) {
+               LOG(DEBUG, "cannot write dotconfig file\n");
+               goto fail;
+       }
+
+       /* write the autoconf.h file */
+       ret = cfzy_c_hdr_write(conftree, "/tmp/autoconf.h");
+       if (ret < 0) {
+               LOG(DEBUG, "cannot write autoconf.h file\n");
+               goto fail;
+       }
+
+       /* write the config/foo/bar.h files */
+       ret = cfzy_multi_c_hdr_write(conftree, "/tmp/config");
+       if (ret < 0) {
+               LOG(DEBUG, "cannot write multiple headers file\n");
+               goto fail;
+       }
+
+       /* test a valid config */
+       if (test_one_dotconfig(conftree, "/tmp/dotconfig", SUCCESS) < 0)
+               goto fail;
+
+       snprintf(filename, sizeof(filename),
+                "%s/../../src/confizery-test/test-configs/"
+                "dotconfig", progdir);
+       if (test_one_dotconfig(conftree, filename, SUCCESS) < 0)
+               goto fail;
+
+       /* invalid configs */
+
+       snprintf(filename, sizeof(filename),
+                "%s/../../src/confizery-test/invalid-configs/"
+                "dotconfig-bad-val", progdir);
+       if (test_one_dotconfig(conftree, filename, CANNOT_READ_DOTCONFIG) < 0)
+               goto fail;
+
+       snprintf(filename, sizeof(filename),
+                "%s/../../src/confizery-test/invalid-configs/"
+                "dotconfig-bad-val2", progdir);
+       if (test_one_dotconfig(conftree, filename, CANNOT_READ_DOTCONFIG) < 0)
+               goto fail;
+
+       snprintf(filename, sizeof(filename),
+                "%s/../../src/confizery-test/invalid-configs/"
+                "dotconfig-bad-val3", progdir);
+       if (test_one_dotconfig(conftree, filename, CANNOT_READ_DOTCONFIG) < 0)
+               goto fail;
+
+       cfzy_conftree_free(conftree);
+       free(progdir);
+       return 0;
+
+ fail:
+       if (conftree != NULL)
+               cfzy_conftree_free(conftree);
+       free(progdir);
+       return -1;
+}
diff --git a/tools/cfzy/cfzy-test/test_dotconfig.h b/tools/cfzy/cfzy-test/test_dotconfig.h
new file mode 100644 (file)
index 0000000..f2f0df9
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TEST_DOTCONFIG_H_
+#define _TEST_DOTCONFIG_H_
+
+/* test cfzy_dotconfig module */
+int test_dotconfig(const char *progname);
+
+#endif
diff --git a/tools/cfzy/cfzy-test/test_expr.c b/tools/cfzy/cfzy-test/test_expr.c
new file mode 100644 (file)
index 0000000..ec0af55
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <cfzy_expr.h>
+
+#include "test_expr.h"
+
+#define SUCCESS 0
+#define CANNOT_PARSE -1
+#define CANNOT_CONVERT_TO_STR -2
+#define IN_AND_OUT_DIFFERS -3
+#define CANNOT_EVAL -4
+#define CANNOT_GET_VARS -5
+
+/* all variables starting with "A" are evaluated to 1, the other are
+ * evaluated to 0. The variable names "unknown" returns an error
+ * (variable not found). */
+static int getvalue(const char *varname, void *arg)
+{
+       (void)arg;
+
+       if (!strcmp(varname, "unknown"))
+               return -1;
+
+       if (varname[0] == 'A')
+               return 1;
+       return 0;
+}
+
+static int test_one_expr(const char *in)
+{
+       char out[256];
+       struct cfzy_expr *exp;
+       int ret;
+
+       /* valid expression */
+       exp = cfzy_expr_parse(in);
+       if (exp == NULL)
+               return CANNOT_PARSE;
+
+       cfzy_expr_graph_dump("test.graph", exp);
+       ret = cfzy_expr_to_str(exp, out, sizeof(out));
+       if (ret < 0) {
+               cfzy_expr_free(exp);
+               return CANNOT_CONVERT_TO_STR;
+       }
+
+       if (strncmp(in, out, sizeof(out))) {
+               cfzy_expr_free(exp);
+               return IN_AND_OUT_DIFFERS;
+       }
+
+       ret = cfzy_expr_eval(exp, getvalue, NULL);
+       if (ret < 0) {
+               cfzy_expr_free(exp);
+               return CANNOT_EVAL;
+       }
+
+       cfzy_expr_free(exp);
+       return ret;
+}
+
+static void print_error(const char *in, int err)
+{
+       printf("Unexpected return value when parsing: <%s>\n", in);
+       switch(err) {
+               case 0:
+               case 1:
+                       printf("Test returned %d, but should not\n", err);
+                       break;
+               case CANNOT_PARSE:
+                       printf("Cannot parse expression\n");
+                       break;
+               case CANNOT_CONVERT_TO_STR:
+                       printf("Cannot convert expression to string\n");
+                       break;
+               case IN_AND_OUT_DIFFERS:
+                       printf("Input and output expressions differ\n");
+                       break;
+               case CANNOT_EVAL:
+                       printf("Cannot evaluate expression\n");
+                       break;
+               case CANNOT_GET_VARS:
+                       printf("Cannot get variable list\n");
+                       break;
+               default:
+                       printf("Invalid error %d\n", err);
+                       break;
+       }
+}
+
+/* must be called with an expression containing only A and B as vars */
+static int test_vars(const char *in)
+{
+       struct cfzy_expr *exp;
+       struct cfzy_list_elt *e;
+       struct cfzy_list_head *list;
+       int ret = 0, count = 0;
+
+       /* valid expression */
+       exp = cfzy_expr_parse(in);
+       if (exp == NULL)
+               return CANNOT_PARSE;
+
+       list = cfzy_expr_get_vars(exp);
+       if (list == NULL) {
+               cfzy_expr_free(exp);
+               return CANNOT_GET_VARS;
+       }
+
+       /* list must contain 2 elements: A and B */
+       TAILQ_FOREACH(e, list, next) {
+               count++;
+               if (!strcmp(e->ptr, "A"))
+                       continue;
+               if (!strcmp(e->ptr, "B"))
+                       continue;
+               ret = CANNOT_GET_VARS;
+       }
+       if (count != 2)
+               ret = CANNOT_GET_VARS;
+
+       cfzy_list_free(list, free);
+       cfzy_expr_free(exp);
+       return ret;
+}
+
+int test_expr(void)
+{
+       int ret = 0, err;
+       const char *in;
+
+       in = "true && true";
+       err = test_one_expr(in);
+       if (err != 1) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       in = "(true || false) && false";
+       err = test_one_expr(in);
+       if (err != 0) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       in = "true || (false && false)";
+       err = test_one_expr(in);
+       if (err != 1) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* && has priority */
+       in = "true || false && false";
+       err = test_one_expr(in);
+       if (err != 1) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* && has priority */
+       in = "false || true && false && true || true && false";
+       err = test_one_expr(in);
+       if (err != 0) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* '==' has priority */
+       in = "false == true && false";
+       err = test_one_expr(in);
+       if (err != 0) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       in = "A";
+       err = test_one_expr(in);
+       if (err != 1) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       in = "B";
+       err = test_one_expr(in);
+       if (err != 0) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       in = "true && (true)";
+       err = test_one_expr(in);
+       if (err != 1) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       in = "(!(A1 && !(B1 || A2 && B2 || A_aa)) || "
+               "(x && !(D && !A) && !(D || E))) == true";
+       err = test_one_expr(in);
+       if (err != 1) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* invalid expression */
+       in = "  ";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* invalid expression */
+       in = "(";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* invalid expression */
+       in = ")";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* invalid expression */
+       in = "( )";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* invalid expression */
+       in = "A &&";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+       /* invalid expression */
+       in = "A && && B";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* invalid expression */
+       in = "A B";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* invalid expression */
+       in = "A && B || C D";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* invalid expression */
+       in = "A (B)";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* invalid expression */
+       in = "&& A B";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* invalid expression */
+       in = "! A B";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* invalid expression */
+       in = "A && !|| D";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* invalid expression */
+       in = "A && !";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* invalid expression, variable must start with a letter */
+       in = "A && _B";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* invalid expression, variable must start with a letter and
+        * numbers are not allowed (at least today) */
+       in = "A == 4";
+       err = test_one_expr(in);
+       if (err != CANNOT_PARSE) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* valid expression, but unknown variable */
+       in = "unknown";
+       err = test_one_expr(in);
+       if (err != CANNOT_EVAL) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       /* valid expression, but won't display like this because
+        * spaces are stripped */
+       in = " true||(false    && \n false)";
+       err = test_one_expr(in);
+       if (err != IN_AND_OUT_DIFFERS) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       err = test_vars("A && B || true && A == !B");
+       if (err != 0) {
+               print_error(in, err);
+               ret = -1;
+       }
+
+       return ret;
+}
diff --git a/tools/cfzy/cfzy-test/test_expr.h b/tools/cfzy/cfzy-test/test_expr.h
new file mode 100644 (file)
index 0000000..cfce881
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TEST_EXPR_H_
+#define _TEST_EXPR_H_
+
+/* test cfzy_expr module */
+int test_expr(void);
+
+#endif
diff --git a/tools/cfzy/libconfizery/Makefile b/tools/cfzy/libconfizery/Makefile
new file mode 100644 (file)
index 0000000..9343477
--- /dev/null
@@ -0,0 +1,29 @@
+include $(TOPDIR)/confizery.vars.mk
+
+LIB = libconfizery
+
+#INSTALL_HEADERS  = cmdline.h
+
+SRCS  = cfzy_expr.c
+SRCS += cfzy_list.c
+SRCS += cfzy_htable.c
+SRCS += cfzy_string.c
+SRCS += cfzy_log.c
+SRCS += cfzy_confnode.c
+SRCS += cfzy_conftree.c
+SRCS += cfzy_dotconfig.c
+SRCS += cfzy_c_hdr.c
+SRCS += cfzy_file.c
+SRCS += cfzy_conftree_parser.c
+SRCS += cfzy_confnode_choice.c
+SRCS += cfzy_confnode_choiceconfig.c
+SRCS += cfzy_confnode_comment.c
+SRCS += cfzy_confnode_config.c
+SRCS += cfzy_confnode_if.c
+SRCS += cfzy_confnode_intconfig.c
+SRCS += cfzy_confnode_menu.c
+SRCS += cfzy_confnode_menuconfig.c
+SRCS += cfzy_confnode_root.c
+SRCS += cfzy_confnode_strconfig.c
+
+include $(TOPDIR)/confizery.lib.mk
diff --git a/tools/cfzy/libconfizery/cfzy_c_hdr.c b/tools/cfzy/libconfizery/cfzy_c_hdr.c
new file mode 100644 (file)
index 0000000..bb75900
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "cfzy_log.h"
+#include "cfzy_file.h"
+#include "cfzy_conftree.h"
+#include "cfzy_confnode.h"
+
+#define LOG(level, fmt, args...)                               \
+       CFZY_LOG("c_hdr", level, fmt, ##args)
+
+/* does not clean directories on error, if path exist, assume it's a
+ * directory and ignore error */
+int cfzy_recursive_mkdir(const char *path)
+{
+       char tmp[PATH_MAX];
+       char *p = NULL;
+       int n, ret;
+
+       n = snprintf(tmp, sizeof(tmp),"%s", path);
+       if (n <= 0 || n >= PATH_MAX)
+               return -1;
+
+       /* ignore all trailing '/' */
+       while (n > 0 && tmp[--n] == '/')
+               tmp[n] = '\0';
+
+       /* create all intermediate directories */
+       for (p = tmp + 1; *p; p++) {
+               if (*p != '/')
+                       continue;
+
+               *p = '\0';
+               if (mkdir(tmp, S_IRWXU) < 0 && errno != EEXIST)
+                       return -1;
+
+               *p = '/';
+       }
+
+       /* and last one */
+       ret = mkdir(tmp, S_IRWXU);
+       if (ret < 0 && errno == EEXIST)
+               return 0;
+
+       return ret;
+}
+
+/*
+ * Get the file name from a config name. Replace first underscore
+ * only:
+ *   CONFIG_A_B -> config/a/b.h
+ *   CONFIG_A__B___C -> config/a/_b/__c.h
+ */
+static char *configname_to_filename(const char *name)
+{
+       char *filename, *s;
+       int len;
+
+       /* allocate room for dst buffer */
+       len = strlen(name);
+       filename = malloc(len + 3);
+       if (filename == NULL)
+               return NULL;
+       strncpy(filename, name, len + 1);
+
+       for (s = filename; *s != '\0'; s++) {
+               /* '/' is not allowed in variable name */
+               if (*s == '/') {
+                       free(filename);
+                       return NULL;
+               }
+
+               /* convert the first '_' in '/' */
+               if (*s == '_') {
+                       *s = '/';
+                       while (s[1] == '/')
+                               s++;
+                       continue;
+               }
+
+               if (isalpha(*s))
+                       *s = tolower(*s);
+       }
+       *s++ = '.';
+       *s++ = 'h';
+       *s = '\0';
+
+       return filename;
+}
+
+static int __cfzy_multi_c_hdr_write(const struct cfzy_confnode *n)
+{
+       const struct cfzy_confnode *c;
+       FILE *f = NULL;
+       char *filename = NULL;
+       char *dname_buf = NULL, *dname_ptr = NULL;
+       char oldval[512];
+       char newval[512];
+       int ret, val;
+
+       /* only dump the node if it has a name and a value */
+       if (n->name != NULL && n->effective_value != NULL) {
+               /* get the name of the file (full path) */
+               filename = configname_to_filename(n->name);
+               if (filename == NULL)
+                       goto fail;
+
+               /* get directory name from file name */
+               dname_buf = strdup(filename);
+               if (dname_buf == NULL)
+                       goto fail;
+               dname_ptr = dirname(dname_buf);
+
+               /* create dirs */
+               if (cfzy_recursive_mkdir(dname_ptr) < 0)
+                       goto fail;
+
+               /* get old value */
+               memset(oldval, 0, sizeof(oldval));
+               f = fopen(filename, "r");
+               if (f != NULL) {
+                       ret = fread(oldval, 1, sizeof(oldval) - 1, f);
+                       if (ret < 0) {
+                               LOG(ERR, "cannot read <%s>\n", filename);
+                               goto fail;
+                       }
+                       fclose(f);
+                       f = NULL;
+               }
+
+               /* if config node is a bool, we have NULL and n will
+                * output the same val (is not st) */
+               if (n->flags & CFZY_F_VAL_IS_BOOL) {
+                       val = cfzy_confnode_str2bool(n, n->effective_value);
+                       if (val < 0)
+                               return -1;
+
+                       /* bool is false -> not set */
+                       if (val == 0) {
+                               if (snprintf(newval, sizeof(newval),
+                                            "/* %s is not set */",
+                                            cfzy_confnode_name(n)) < 0)
+                                       goto fail;
+                       }
+                       else {
+                               /* bool is true */
+                               if (snprintf(newval, sizeof(newval),
+                                            "/* %s = %s */", cfzy_confnode_name(n),
+                                            n->effective_value) < 0)
+                                       goto fail;
+                       }
+               }
+               else {
+                       if (snprintf(newval, sizeof(newval),
+                                    "/* %s = %s */", cfzy_confnode_name(n),
+                                    n->effective_value) < 0)
+                               goto fail;
+               }
+
+               /* if the value is different, write the new value */
+               if (strncmp(newval, oldval, sizeof(newval)) != 0) {
+                       f = fopen(filename, "w");
+                       if (f == NULL) {
+                               LOG(ERR, "cannot open file <%s>\n", filename);
+                               goto fail;
+                       }
+
+                       if (fprintf(f, "%s", newval) < 0) {
+                               LOG(ERR, "cannot write in <%s>\n",
+                                   filename);
+                               goto fail;
+                       }
+                       fclose(f);
+                       f = NULL;
+               }
+
+               free(dname_buf);
+               free(filename);
+       }
+
+       /* dump all the children of root node */
+       TAILQ_FOREACH(c, &n->children, child_next) {
+               if (__cfzy_multi_c_hdr_write(c) < 0)
+                       break;
+       }
+
+       return 0;
+
+ fail:
+       if (f != NULL)
+               fclose(f);
+       if (dname_buf != NULL)
+               free(dname_buf);
+       if (filename != NULL)
+               free(filename);
+
+       return -1;
+}
+
+/* write config/foo/bar.h */
+int cfzy_multi_c_hdr_write(const struct cfzy_conftree *conftree,
+                          const char *basedir)
+{
+       char old_cwd_buf[PATH_MAX];
+       char *old_cwd = NULL;
+       const struct cfzy_confnode *c;
+       int ret = 0;
+
+       LOG(INFO, "multi C headers write <%s>\n", basedir);
+
+       if (cfzy_recursive_mkdir(basedir) < 0)
+               return -1;
+
+       old_cwd = getcwd(old_cwd_buf, sizeof(old_cwd_buf));
+       if (old_cwd == NULL) {
+               LOG(ERR, "getcwd failed: %s\n", strerror(errno));
+               return -1;
+       }
+
+       if (chdir(basedir) < 0) {
+               LOG(ERR, "chdir failed: %s\n", strerror(errno));
+               return -1;
+       }
+
+       /* dump all the children of root node */
+       TAILQ_FOREACH(c, &conftree->root->children, child_next) {
+               ret = __cfzy_multi_c_hdr_write(c);
+               if (ret < 0)
+                       break;
+       }
+
+       if (chdir(old_cwd) < 0)
+               LOG(ERR, "chdir back to <%s> failed: %s\n",
+                   old_cwd, strerror(errno));
+
+       return ret;
+}
+
+/* write config/autoconf.h */
+int cfzy_c_hdr_write(const struct cfzy_conftree *conftree,
+                    const char *filename)
+{
+       FILE *tmp_f, *f;
+       const struct cfzy_confnode *c;
+       int ret = 0;
+
+       LOG(INFO, "open old C header <%s>\n", filename);
+
+       /* open old file in read mode */
+
+       f = fopen(filename, "r");
+       if (f == NULL && errno != ENOENT) {
+               printf("cannot open file <%s>: %s\n",
+                      filename, strerror(errno));
+               return -1;
+       }
+
+       /* old file exists */
+       if (f != NULL) {
+
+               /* write C header in temp file */
+               tmp_f = tmpfile();
+               if (tmp_f == NULL) {
+                       printf("cannot open temp file\n");
+                       return -1;
+               }
+
+               /* dump all the children of root node */
+               TAILQ_FOREACH(c, &conftree->root->children, child_next) {
+                       ret = cfzy_confnode_c_hdr_write(c, tmp_f);
+                       if (ret < 0)
+                               break;
+               }
+
+               /* files are the same, nothing to do */
+               if (cfzy_file_compare(f, tmp_f) == 0) {
+                       LOG(INFO, "already up to date: <%s>\n", filename);
+                       fclose(tmp_f);
+                       fclose(f);
+                       return 0;
+               }
+
+               fclose(tmp_f);
+               fclose(f);
+       }
+
+       /* reopen the file in write mode */
+
+       LOG(INFO, "write new C header <%s>\n", filename);
+
+       f = fopen(filename, "w");
+       if (f == NULL) {
+               printf("cannot open file <%s>\n", filename);
+               return -1;
+       }
+
+       /* dump all the children of root node */
+       TAILQ_FOREACH(c, &conftree->root->children, child_next) {
+               ret = cfzy_confnode_c_hdr_write(c, f);
+               if (ret < 0)
+                       break;
+       }
+
+       fclose(f);
+
+       return ret;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_c_hdr.h b/tools/cfzy/libconfizery/cfzy_c_hdr.h
new file mode 100644 (file)
index 0000000..841108d
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * Confizery C header generation
+ *
+ * This module provides the public API to generate the C header files
+ * from the configuration tree.
+ */
+
+#ifndef _CFZY_C_HDR_H_
+#define _CFZY_C_HDR_H_
+
+/**
+ * Write configuration in a C header file
+ *
+ * Write a C header (autoconf.h like) in a file. This single file will
+ * contain a list of #define (one per option) and is desgined to be
+ * included by a C application using this config.
+ *
+ * If the file already exists and has the same content, nothing is
+ * written in it.
+ *
+ * @param conftree
+ *   the configuration tree
+ * @param filename
+ *   name of the output file
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_c_hdr_write(const struct cfzy_conftree *conftree,
+                    const char *filename);
+
+/**
+ * Write the configuration in multiple header files
+ *
+ * Write a C header (autoconf.h like) in a file. Each option is
+ * written in one single file. If one file already exists and has the
+ * same content, nothing is written in it.
+ *
+ * These files are not desgined to be
+ * included by a C application as each option is just included as a C
+ * comment. The goal of these files is to handle the dependencies
+ * properly in the build system.
+ *
+ * @param conftree
+ *   the configuration tree
+ * @param filename
+ *   name of the output file
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_multi_c_hdr_write(const struct cfzy_conftree *conftree,
+                          const char *basedir);
+
+#endif
diff --git a/tools/cfzy/libconfizery/cfzy_confnode.c b/tools/cfzy/libconfizery/cfzy_confnode.c
new file mode 100644 (file)
index 0000000..fc6e6c9
--- /dev/null
@@ -0,0 +1,859 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/queue.h>
+
+#include "cfzy_log.h"
+#include "cfzy_list.h"
+#include "cfzy_htable.h"
+#include "cfzy_string.h"
+#include "cfzy_expr.h"
+#include "cfzy_confnode.h"
+#include "cfzy_conftree.h"
+
+#define LOG(level, fmt, args...)                               \
+       CFZY_LOG("confnode", level, fmt, ##args)
+
+/* alloc a new confnode */
+struct cfzy_confnode *cfzy_confnode_alloc(struct cfzy_conftree *conftree)
+{
+       struct cfzy_confnode *n;
+
+       n = malloc(sizeof(struct cfzy_confnode));
+       if (n == NULL)
+               return NULL;
+
+       memset(n, 0, sizeof(*n));
+       TAILQ_INIT(&n->children);
+
+       n->conftree = conftree;
+
+       n->expr_deps = cfzy_list_alloc();
+       if (n->expr_deps == NULL) {
+               free(n);
+               return NULL;
+       }
+
+       n->default_val_list = cfzy_list_alloc();
+       if (n->default_val_list == NULL) {
+               cfzy_list_free(n->expr_deps, NULL);
+               free(n);
+               return NULL;
+       }
+
+       return n;
+}
+
+/* Free the node given as argument, and all its associated data */
+void cfzy_confnode_free(struct cfzy_confnode *n)
+{
+       struct cfzy_confnode *c;
+       struct cfzy_list_elt *e;
+       struct cfzy_confnode_defvalue *defval;
+
+       /* free the children */
+       while ((c = TAILQ_FIRST(&n->children)) != NULL) {
+               TAILQ_REMOVE(&n->children, c, child_next);
+               cfzy_confnode_free(c);
+       }
+
+       /* do the specific free first */
+       if (n->ops && n->ops->free)
+               n->ops->free(n);
+
+       /* free the deps expression list */
+       cfzy_list_free(n->expr_deps, (void *)cfzy_expr_free);
+
+       /* free the deps expression list (manually free each element
+        * instead of providing a function) */
+       while ((e = TAILQ_FIRST(n->default_val_list)) != NULL) {
+               TAILQ_REMOVE(n->default_val_list, e, next);
+               defval = e->ptr;
+               cfzy_expr_free(defval->expr);
+               free(defval->val);
+               free(defval);
+               free(e);
+       }
+       cfzy_list_free(n->default_val_list, NULL);
+
+       if (n->node_deps != NULL)
+               cfzy_list_free(n->node_deps, NULL);
+
+       /* free the name, help, prompt, ... */
+       if (n->name)
+               free(n->name);
+       if (n->prompt)
+               free(n->prompt);
+       if (n->help)
+               free(n->help);
+       if (n->default_value)
+               free(n->default_value);
+       if (n->user_value)
+               free(n->user_value);
+       if (n->effective_value)
+               free(n->effective_value);
+       if (n->old_user_value)
+               free(n->old_user_value);
+       if (n->old_effective_value)
+               free(n->old_effective_value);
+       if (n->filename)
+               free(n->filename);
+
+       free(n);
+}
+
+/* return the list of nodes required to enable this one */
+struct cfzy_list_head *
+cfzy_confnode_get_deplist(const struct cfzy_confnode *n)
+{
+       struct cfzy_list_head *node_list = NULL, *var_list= NULL;
+       struct cfzy_list_elt *e, *e2;
+       struct cfzy_expr *expr;
+       struct cfzy_confnode *c;
+       const char *varname;
+       const struct cfzy_confnode_defvalue *defval;
+       const struct cfzy_conftree *conftree;
+
+       node_list = cfzy_list_alloc();
+       if (node_list == NULL) {
+               LOG(ERR, "cannot allocate node list\n");
+               return NULL;
+       }
+
+       /* get associated configuration tree */
+       conftree = n->conftree;
+
+       /* list of dependencies (expression list) */
+       TAILQ_FOREACH(e, n->expr_deps, next) {
+
+               expr = e->ptr;
+
+               /* get list of variables for this expression */
+               var_list = cfzy_expr_get_vars(expr);
+               if (var_list == NULL) {
+                       LOG(ERR, "cannot allocate get list\n");
+                       goto fail;
+               }
+
+               /* list of variables in this expression */
+               TAILQ_FOREACH(e2, var_list, next) {
+
+                       varname = e2->ptr;
+
+                       c = cfzy_htable_lookup(conftree->nodes_htable, varname);
+                       if (c == NULL) {
+                               LOG(ERR, "cannot find node <%s>\n", varname);
+                               goto fail;
+                       }
+
+                       if (cfzy_list_elt_is_in_list(node_list, c))
+                               continue;
+
+                       if (cfzy_list_add_tail(node_list, c) < 0) {
+                               LOG(ERR, "cannot add node in list\n");
+                               goto fail;
+                       }
+               }
+
+               cfzy_list_free(var_list, free);
+               var_list = NULL;
+       }
+
+       /* list of conditional default values depending on an expression */
+       TAILQ_FOREACH(e, n->default_val_list, next) {
+
+               defval = e->ptr;
+               expr = defval->expr;
+
+               /* get list of variables for this expression */
+               var_list = cfzy_expr_get_vars(expr);
+               if (var_list == NULL) {
+                       LOG(ERR, "cannot allocate get list\n");
+                       goto fail;
+               }
+
+               /* list of variables in this expression */
+               TAILQ_FOREACH(e2, var_list, next) {
+
+                       varname = e2->ptr;
+
+                       c = cfzy_htable_lookup(conftree->nodes_htable, varname);
+                       if (c == NULL) {
+                               LOG(ERR, "cannot find node <%s>\n", varname);
+                               goto fail;
+                       }
+
+                       if (cfzy_list_elt_is_in_list(node_list, c))
+                               continue;
+
+                       if (cfzy_list_add_tail(node_list, c) < 0) {
+                               LOG(ERR, "cannot add node in list\n");
+                               goto fail;
+                       }
+               }
+
+               cfzy_list_free(var_list, free);
+               var_list = NULL;
+       }
+
+       /* parent */
+       if (n->parent != NULL && !cfzy_list_elt_is_in_list(node_list, n->parent)) {
+
+               if (cfzy_list_add_tail(node_list, n->parent) < 0) {
+                       LOG(ERR, "cannot add node in list\n");
+                       goto fail;
+               }
+       }
+
+       return node_list;
+
+ fail:
+       if (node_list != NULL)
+               cfzy_list_free(node_list, NULL);
+       if (var_list != NULL)
+               cfzy_list_free(var_list, free);
+
+       return NULL;
+}
+
+/* Add a conditional default value to a node */
+int cfzy_confnode_add_defval(struct cfzy_confnode *n, const char *val,
+                            const char *expr_buf)
+{
+       struct cfzy_confnode_defvalue *defval;
+
+       defval = malloc(sizeof(*defval));
+       if (defval == NULL)
+               return -1;
+
+       memset(defval, 0, sizeof(*defval));
+       defval->expr = cfzy_expr_parse(expr_buf);
+       if (defval->expr == NULL) {
+               free(defval);
+               return -1;
+       }
+
+       defval->val = strdup(val);
+       if (defval->val == NULL) {
+               cfzy_expr_free(defval->expr);
+               free(defval);
+               return -1;
+       }
+
+       if (cfzy_list_add_tail(n->default_val_list, defval) < 0) {
+               free(defval->val);
+               cfzy_expr_free(defval->expr);
+               free(defval);
+               return -1;
+       }
+
+       return 0;
+}
+
+/* Parse a line structure (and its associated line buffer) to match an
+ * attribute of a confnode. Return 0 if the line matches, else return
+ * -1 if we cannot match a valid attribute. */
+enum cfzy_parse_return
+cfzy_confnode_add_attr(struct cfzy_confnode *n,
+                      const struct cfzy_token_list *tklist)
+{
+       enum cfzy_parse_return ret;
+       struct cfzy_token *first_tok, *tok;
+       const char *linebuf = tklist->linebuf;
+
+       first_tok = TAILQ_FIRST(&tklist->list);
+
+       /* specific attribute */
+       if (n->ops->add_attr != NULL) {
+               ret = n->ops->add_attr(n, tklist, linebuf);
+               if (ret != NO_MATCH)
+                       return ret;
+       }
+
+       /* syntax is: "prompt content" */
+       if (tklist->n_token == 2 &&
+           !strcmp(first_tok->str, "prompt")) {
+
+               if (n->flags & CFZY_F_NO_SET_PROMPT) {
+                       LOG(ERR, "node does not support 'prompt' attr\n");
+                       return ERROR;
+               }
+
+               tok = TAILQ_NEXT(first_tok, next);
+               if (n->prompt != NULL) {
+                       free(n->prompt);
+                       n->prompt = NULL;
+               }
+               n->prompt = strdup(tok->str);
+               if (n->prompt == NULL) {
+                       LOG(ERR, "cannot allocate prompt\n");
+                       return ERROR;
+               }
+               return SUCCESS;
+       }
+
+       /* syntax is: "requires EXPRESSION" */
+       if (tklist->n_token > 1 &&
+           !strcmp(first_tok->str, "requires")) {
+               struct cfzy_expr *exp;
+
+               if (n->flags & CFZY_F_NO_SET_DEPS) {
+                       LOG(ERR, "node does not support 'requires' attr\n");
+                       return ERROR;
+               }
+
+               tok = TAILQ_NEXT(first_tok, next);
+               exp = cfzy_expr_parse(linebuf + tok->offset);
+               if (exp == NULL) {
+                       LOG(ERR, "cannot parse expression\n");
+                       return ERROR;
+               }
+
+               cfzy_list_add_tail(n->expr_deps, exp);
+               return SUCCESS;
+       }
+
+       /* syntax is: "default VALUE" */
+       if (tklist->n_token == 2 &&
+           !strcmp(first_tok->str, "default")) {
+
+               if (n->flags & CFZY_F_NO_SET_DEFAULT) {
+                       LOG(ERR, "node does not support 'default' attr\n");
+                       return ERROR;
+               }
+
+               tok = TAILQ_NEXT(first_tok, next);
+               if (cfzy_confnode_str2bool(n, tok->str) < 0) {
+                       LOG(ERR, "invalid value for this node\n");
+                       return ERROR;
+               }
+
+               if (n->default_value != NULL)
+                       free(n->default_value);
+               n->default_value = strdup(tok->str);
+               if (n->default_value == NULL) {
+                       LOG(ERR, "cannot add default value\n");
+                       return ERROR;
+               }
+
+               return SUCCESS;
+       }
+
+       /* syntax is: "default VALUE if EXPR" */
+       if (tklist->n_token >= 4 &&
+           !strcmp(first_tok->str, "default")) {
+               struct cfzy_token *tok2;
+
+               tok = TAILQ_NEXT(first_tok, next); /* points to value */
+               tok2 = TAILQ_NEXT(tok, next); /* points to "if" */
+
+               if (strcmp(tok2->str, "if"))
+                       return NO_MATCH;
+
+               if (n->flags & CFZY_F_NO_SET_DEFAULT) {
+                       LOG(ERR, "node does not support 'default' attr\n");
+                       return ERROR;
+               }
+
+               tok2 = TAILQ_NEXT(tok2, next); /* points to expression */
+
+               if (cfzy_confnode_str2bool(n, tok->str) < 0) {
+                       LOG(ERR, "invalid value for this node\n");
+                       return ERROR;
+               }
+
+               if (cfzy_confnode_add_defval(n, tok->str,
+                                            linebuf + tok2->offset) < 0) {
+                       LOG(ERR, "cannot add default value\n");
+                       return ERROR;
+               }
+
+               return SUCCESS;
+       }
+
+       return NO_MATCH;
+}
+
+/* write config value in file f in a dotconfig-like manner. Return 0
+ * on success. */
+int cfzy_confnode_dotconfig_write(const struct cfzy_confnode *n, FILE *f)
+{
+       int val;
+       const struct cfzy_confnode *c;
+
+       if (n->ops->dotconfig_write)
+               return n->ops->dotconfig_write(n, f);
+
+       if (n->flags & CFZY_F_DOTCONF_SHOW_TITLE) {
+               if (fprintf(f,
+                           "#\n"
+                           "# -- %s\n"
+                           "#\n", n->prompt) < 0)
+                       return -1;
+       }
+
+       /* only dump the children if effective value is not defined
+        * (some nodes like "comment", "if", "menu", ... can have a
+        * NULL effective value) */
+       if (n->effective_value == NULL)
+               goto dump_children;
+
+       /* when a boolean is unset, use the "is not set" syntax */
+       if (n->flags & CFZY_F_VAL_IS_BOOL) {
+               val = cfzy_confnode_str2bool(n, n->effective_value);
+               if (val < 0)
+                       return -1;
+
+               if (val == 0) {
+                       if (fprintf(f, "# CONFIG_%s is not set\n",
+                                   cfzy_confnode_name(n)) < 0)
+                               return -1;
+                       return 0;
+               }
+       }
+
+       /* common case: dump the value, with or without quotes */
+       if (n->flags & CFZY_F_QUOTE_VAL) {
+               char *quoted_value = cfzy_string_quote(n->effective_value);
+
+               if (quoted_value == NULL)
+                       return -1;
+
+               if (fprintf(f, "CONFIG_%s=%s\n", cfzy_confnode_name(n),
+                           quoted_value) < 0) {
+                       free(quoted_value);
+                       return -1;
+               }
+               free(quoted_value);
+       }
+       else {
+               if (fprintf(f, "CONFIG_%s=%s\n", cfzy_confnode_name(n),
+                           n->effective_value) < 0)
+                       return -1;
+       }
+
+ dump_children:
+       TAILQ_FOREACH(c, &n->children, child_next) {
+               if (cfzy_confnode_dotconfig_write(c, f) < 0)
+                       return -1;
+       }
+       return 0;
+}
+
+/* write config value in file f in a dotconfig-like manner. Return 0
+ * on success. */
+int cfzy_confnode_c_hdr_write(const struct cfzy_confnode *n, FILE *f)
+{
+       int val;
+       const struct cfzy_confnode *c;
+
+       if (n->ops->c_hdr_write)
+               return n->ops->c_hdr_write(n, f);
+
+       /* if no value, don't dump the value of the node, just the children */
+       if (n->effective_value == NULL)
+               goto dump_children;
+
+       if (n->flags & CFZY_F_VAL_IS_BOOL) {
+               val = cfzy_confnode_str2bool(n, n->effective_value);
+               if (val < 0)
+                       return -1;
+
+               /* bool is false -> undef */
+               if (val == 0) {
+                       if (fprintf(f, "#undef CONFIG_%s\n",
+                                   cfzy_confnode_name(n)) < 0)
+                               return -1;
+                       return 0;
+               }
+
+               /* bool is true -> #define to 1 */
+               if (fprintf(f, "#define CONFIG_%s 1\n",
+                           cfzy_confnode_name(n)) < 0)
+                       return -1;
+       }
+       /* common case: dump the value, with or without quotes */
+       else if (n->flags & CFZY_F_QUOTE_VAL) {
+               char *quoted_value = cfzy_string_quote(n->effective_value);
+
+               if (quoted_value == NULL)
+                       return -1;
+
+               if (fprintf(f, "#define CONFIG_%s %s\n",
+                           cfzy_confnode_name(n), quoted_value) < 0) {
+                       free(quoted_value);
+                       return -1;
+               }
+               free(quoted_value);
+       }
+       else {
+               if (fprintf(f, "#define CONFIG_%s %s\n",
+                           cfzy_confnode_name(n), n->effective_value) < 0)
+                       return -1;
+       }
+
+ dump_children:
+       TAILQ_FOREACH(c, &n->children, child_next) {
+               if (cfzy_confnode_c_hdr_write(c, f) < 0)
+                       return -1;
+       }
+       return 0;
+}
+
+/* Close the node beeing parsed */
+int cfzy_confnode_close(struct cfzy_confnode *n)
+{
+       if (n->ops->close)
+               return n->ops->close(n);
+
+       return 0;
+}
+
+/* Return the boolean value of the node given the string value */
+int cfzy_confnode_str2bool(const struct cfzy_confnode *n,
+                               const char *value)
+{
+       if (n->ops->str2bool)
+               return n->ops->str2bool(n, value);
+
+       /* if there is no specific function, any non-NULL value
+        * returns 1 */
+       if (value == NULL)
+               return 0;
+
+       return 1;
+}
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+const char *cfzy_confnode_get_type_str(const struct cfzy_confnode *n)
+{
+       if (n->ops->get_type_str)
+               return n->ops->get_type_str(n);
+
+       return "unknown";
+}
+
+static int __get_path(const struct cfzy_confnode *n, char *buf,
+                     int len, int first)
+{
+       int ret = 0, off;
+
+       if (n->parent == NULL)
+               return snprintf(buf, len, "/");
+
+       ret = __get_path(n->parent, buf, len, 0);
+       if (ret < 0)
+               return ret;
+       off = ret;
+       if (off >= len)
+               return -1;
+
+       if (n->flags & CFZY_F_INVISIBLE)
+               return ret;
+
+       /* a visible node must have a name */
+       if (n->name == NULL)
+               return -1;
+
+       ret = snprintf(buf + off, len - off, "%s%s", n->name,
+                      first ? "" : "/");
+       if (ret >= len - off)
+               return -1;
+
+       return ret + off;
+}
+
+/* Dump path in buffer. Invisible nodes like 'if' are ignored. */
+int cfzy_confnode_path(const struct cfzy_confnode *n, char *buf, int len)
+{
+       int ret;
+
+       ret = __get_path(n, buf, len, 1);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+/* dump a config file in a file */
+int cfzy_confnode_dump(const struct cfzy_confnode *n, FILE *f, int mode)
+{
+       const struct cfzy_confnode *c;
+       struct cfzy_confnode_defvalue *defval;
+       struct cfzy_list_elt *e;
+       struct cfzy_expr *expr;
+       char buf[512];
+
+       if (fprintf(f, "%s\n", cfzy_confnode_get_type_str(n)) < 0)
+               return -1;
+       if (fprintf(f, "  name: %s\n", cfzy_confnode_name(n)) < 0)
+               return -1;
+       if (fprintf(f, "  prompt: %s\n", n->prompt) < 0)
+               return -1;
+       if (cfzy_confnode_path(n, buf, sizeof(buf)) == 0) {
+               if (fprintf(f, "  path: %s\n", buf) < 0)
+                       return -1;
+       }
+
+       if (mode == CFZY_DUMP_MODE_MINIMAL)
+               return 0;
+
+       /* explicit dependencies */
+       TAILQ_FOREACH(e, n->expr_deps, next) {
+               expr = e->ptr;
+               if (cfzy_expr_to_str(expr, buf, sizeof(buf)) < 0)
+                       return -1;
+               if (fprintf(f, "  requires: %s\n", buf) < 0)
+                       return -1;
+       }
+
+       /* XXX display valid values */
+
+       /* default values */
+       TAILQ_FOREACH(e, n->default_val_list, next) {
+               defval = e->ptr;
+
+               if (cfzy_expr_to_str(defval->expr, buf, sizeof(buf)) < 0)
+                       return -1;
+
+               if (fprintf(f, "  default: %s if %s\n", defval->val, buf) < 0)
+                               return -1;
+       }
+       if (fprintf(f, "  node_default %s\n", n->default_value) < 0)
+               return -1;
+       if (fprintf(f, "  user_value %s\n", n->user_value) < 0)
+               return -1;
+       if (fprintf(f, "  effective_value %s\n", n->effective_value) < 0)
+               return -1;
+
+       if (mode == CFZY_DUMP_MODE_STANDARD)
+               return 0;
+
+       if (fprintf(f, "  old_user_value %s\n", n->old_user_value) < 0)
+               return -1;
+       if (fprintf(f, "  old_effective_value %s\n", n->old_effective_value) < 0)
+               return -1;
+       if (fprintf(f, "  parsed at %s:%d\n", n->filename, n->linenum) < 0)
+               return -1;
+
+       if (fprintf(f, "\n") < 0)
+               return -1;
+
+       if (mode == CFZY_DUMP_MODE_FULL)
+               return 0;
+
+       /* dump children */
+       TAILQ_FOREACH(c, &n->children, child_next) {
+               if (cfzy_confnode_dump(c, f, CFZY_DUMP_MODE_FULL_RECURSIVE) < 0)
+                       return -1;
+       }
+
+       /* close node if it's a directory */
+       if (n->flags & CFZY_F_IS_DIR) {
+               if (fprintf(f, "end%s\n\n", cfzy_confnode_get_type_str(n)) < 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+/* set the uservalue of a node */
+int cfzy_confnode_set_uservalue(struct cfzy_confnode *n, const char *value)
+{
+       char *newvalue = NULL;
+
+       if (n->ops->set_uservalue)
+               return n->ops->set_uservalue(n, value);
+
+       /* check if value is valid */
+       if (cfzy_confnode_str2bool(n, value) < 0)
+               return -1;
+
+       if (value != NULL) {
+               newvalue = strdup(value);
+               if (newvalue == NULL) {
+                       LOG(ERR, "cannot allocate new value\n");
+                       return -1;
+               }
+       }
+
+       if (n->user_value != NULL)
+               free(n->user_value);
+
+       n->user_value = newvalue;
+       return 0;
+}
+
+/* Get the boolean value of a node, given its name, used as a callback
+ * by cfzy_expr_parse() when parsing an expression for
+ * cfzy_confnode_evaluate() */
+int cfzy_confnode_get_boolvalue(const char *name,
+                               const struct cfzy_conftree *conftree)
+{
+       const struct cfzy_confnode *n;
+
+       n = cfzy_htable_lookup(conftree->nodes_htable, name);
+       if (n == NULL)
+               return -1;
+
+       return cfzy_confnode_str2bool(n, n->effective_value);
+}
+
+/* return 0 if a parent node is disabled or if a prerequisite
+ * expression is false, else return 1. On error, return -1. */
+int cfzy_confnode_check_deps(struct cfzy_confnode *n)
+{
+       struct cfzy_list_elt *e;
+       struct cfzy_expr *expr;
+       struct cfzy_confnode *parent;
+       int (*get_boolvalue)(const char *, const struct cfzy_conftree *);
+       int (*get_boolvalue_casted)(const char *, void *);
+       int ret;
+
+       /* we use an intermediate variable to have a compiler warning
+        * if the prototype of cfzy_confnode_get_boolvalue changes. We
+        * know that that these types are compatible here. */
+       get_boolvalue = cfzy_confnode_get_boolvalue;
+       get_boolvalue_casted = (void *)get_boolvalue;
+
+       /* check the dep list first */
+       TAILQ_FOREACH(e, n->expr_deps, next) {
+               expr = e->ptr;
+
+               ret = cfzy_expr_eval(expr, get_boolvalue_casted, n->conftree);
+               if (ret < 0)
+                       return -1;
+
+               /* the expression is evaluated to 0, we cannot enable
+                * this node */
+               if (ret == 0) {
+                       n->effective_value = NULL;
+                       LOG(DEBUG, "disable node <%s> (expr dep)\n",
+                           cfzy_confnode_name(n));
+                       return 0;
+               }
+       }
+
+       /* check the parent nodes */
+       for (parent = n->parent; parent != NULL; parent = parent->parent) {
+               ret = cfzy_confnode_str2bool(parent, parent->effective_value);
+               if (ret < 0)
+                       return -1;
+
+               /* a parent node is evaluated to 0, we cannot enable
+                * this node */
+               if (ret == 0) {
+                       n->effective_value = NULL;
+                       LOG(DEBUG, "disable node <%s> (parent dep)\n",
+                           cfzy_confnode_name(n));
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+/* Evaluate the effective value of a node, assuming all prerequisites
+ * are already evaluated. */
+int cfzy_confnode_evaluate(struct cfzy_confnode *n)
+{
+       struct cfzy_list_elt *e;
+       struct cfzy_confnode_defvalue *defval;
+       int (*get_boolvalue)(const char *, const struct cfzy_conftree *);
+       int (*get_boolvalue_casted)(const char *, void *);
+       int ret;
+
+       LOG(DEBUG, "evaluating node <%s>\n", cfzy_confnode_name(n));
+
+       /* we use an intermediate variable to have a compiler warning
+        * if the prototype of cfzy_confnode_get_boolvalue changes. We
+        * know that that these types are compatible here. */
+       get_boolvalue = cfzy_confnode_get_boolvalue;
+       get_boolvalue_casted = (void *)get_boolvalue;
+
+       /* remove previous effective value */
+       if (n->effective_value != NULL) {
+               free(n->effective_value);
+               n->effective_value = NULL;
+       }
+
+       /* check prerequisites and parents */
+       ret = cfzy_confnode_check_deps(n);
+       if (ret == 0) {
+               /* we cannot enable this node due to dependencies */
+               n->effective_value = NULL;
+               LOG(DEBUG, "disable node <%s> (expr dep)\n",
+                   cfzy_confnode_name(n));
+               return 0;
+       }
+       /* error */
+       if (ret < 0)
+               return ret;
+
+       /* user value has the priority */
+       if (n->user_value != NULL) {
+               n->effective_value = strdup(n->user_value);
+               if (n->effective_value == NULL)
+                       return -1;
+
+               LOG(DEBUG, "set node <%s> to user value <%s>\n",
+                   cfzy_confnode_name(n), n->effective_value);
+               return 0;
+       }
+
+       /* assign a default value */
+       TAILQ_FOREACH(e, n->default_val_list, next) {
+               defval = e->ptr;
+
+               ret = cfzy_expr_eval(defval->expr, get_boolvalue_casted,
+                                    n->conftree);
+               if (ret < 0)
+                       return -1;
+
+               /* the expression is evaluated to 1, use this default
+                * value for this node */
+               if (ret == 1) {
+                       n->effective_value = strdup(defval->val);
+                       if (n->effective_value == NULL)
+                               return -1;
+                       LOG(DEBUG, "set node <%s> to default_list value <%s>\n",
+                           cfzy_confnode_name(n), n->effective_value);
+                       return 0;
+               }
+       }
+
+       if (n->default_value != NULL)
+               n->effective_value = strdup(n->default_value);
+
+       LOG(DEBUG, "set node <%s> to default value <%s>\n",
+           cfzy_confnode_name(n), n->effective_value);
+
+       return 0;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_confnode.h b/tools/cfzy/libconfizery/cfzy_confnode.h
new file mode 100644 (file)
index 0000000..0d2f7da
--- /dev/null
@@ -0,0 +1,564 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * Confizery configuration node
+ *
+ * This module provides functions related to configuration nodes. A
+ * configuration node is a structure that stores a part of the
+ * information of the configuration tree. For instance, a "config" or
+ * "menuconfig" option is stored in a configuration node structure.
+ *
+ * It contains among other fields the name of the node (CONFIG_XYZ),
+ * the prompt and the help. Each node references its parent and a list
+ * of children as some nodes can contains some other nodes (menu, if,
+ * choice, ...). It also contains the default values, the user value,
+ * and the effective value of the node. Some nodes have no values (if,
+ * comment, ...).
+ */
+
+#ifndef _CFZY_CONFNODE_H_
+#define _CFZY_CONFNODE_H_
+
+#include <stdio.h>
+
+#include "cfzy_confnode_ops.h"
+#include "cfzy_conftree_parser.h"
+
+/* confnode flags */
+#define CFZY_F_IS_DIR             0x0001 /**< node can contain other nodes */
+#define CFZY_F_NO_SET_DEPS        0x0002 /**< does not support the "requires" attribute */
+#define CFZY_F_NO_SET_PROMPT      0x0004 /**< does not support the "prompt" attribute */
+#define CFZY_F_NO_SET_DEFAULT     0x0008 /**< does not support the "default" attribute */
+#define CFZY_F_IS_ROOT            0x0010 /**< the root node */
+#define CFZY_F_VAL_IS_BOOL        0x0020 /**< value is a boolean */
+#define CFZY_F_INVISIBLE          0x0040 /**< never seen from cmdline (like the IF node) */
+#define CFZY_F_NO_NAME            0x0080 /**< node has no name (if, comments, ...) */
+#define CFZY_F_NO_SET_VALUE       0x0100 /**< user cannot set value (if, comments, ...) */
+#define CFZY_F_QUOTE_VAL          0x0200 /**< quote value */
+#define CFZY_F_DOTCONF_SHOW_TITLE 0x0400 /**< show title as a comment in dotconfig file  */
+
+struct cfzy_conftree;
+
+/**
+ * Structure storing a conditional default value
+ */
+struct cfzy_confnode_defvalue {
+       struct cfzy_expr *expr; /**< expression (condition) */
+       char *val;              /**< value */
+};
+
+/**
+ * List of cfzy_node, used to chain children together
+ */
+TAILQ_HEAD(cfzy_confnode_list, cfzy_confnode);
+
+/**
+ * This structure defines a configuration node in the configration
+ * tree, for instance a "menu" entry, or a "bool" entry.
+ */
+struct cfzy_confnode {
+       /* pointer to other nodes in conf tree */
+       struct cfzy_confnode *parent;          /**< pointer to parent */
+       struct cfzy_confnode_list children;    /**< list of children */
+       TAILQ_ENTRY(cfzy_confnode) child_next; /**< next in children list */
+       struct cfzy_conftree *conftree;        /**< back pointer to conftree */
+
+       /* node attributes */
+       char *name;                        /**< node name (dynamic alloc) */
+       char *prompt;                      /**< node prompt (dynamic alloc) */
+       char *help;                        /**< node help (dynamic alloc) */
+       unsigned flags;                    /**< node flags */
+       struct cfzy_list_head *expr_deps;  /**< list of prereq expressions */
+
+       /* where it was parsed */
+       char *filename;                    /**< name of conftree file */
+       int linenum;                       /**< line where node was declared */
+
+       /* specific to a node type */
+       const struct cfzy_confnode_ops *ops;   /**< specific ops on node */
+       void *private;
+
+       /* values */
+       char *default_value; /**< default value for this node */
+       struct cfzy_list_head *default_val_list; /* conditional default vals */
+       char *user_value;             /**< value wanted by the user */
+       char *effective_value;        /**< real value, taking care of deps */
+       char *old_user_value;         /**< saved user value */
+       char *old_effective_value;    /**< saved effective value */
+
+       /* allow to chain this node in a priority ordered list */
+       TAILQ_ENTRY(cfzy_confnode) prio_next;  /**< next in ordered list */
+       int in_prio_list;             /**< true if present in prio list */
+
+       struct cfzy_list_head *node_deps; /**< list of nodes that must be
+                                          * evaluated before this one */
+};
+
+/**
+ * Get the name of a node
+ *
+ * @param n
+ *   configuration node
+ * @return
+ *   pointer to the node name (hosted in the node structure)
+ */
+static inline const char *cfzy_confnode_name(const struct cfzy_confnode *n)
+{
+       if (n->name == NULL)
+               return "no-name";
+       return n->name;
+}
+
+/**
+ * Allocate a new node
+ *
+ * Allocate a new empty node, with no specific ops.
+ *
+ * @param conftree
+ *   configuration tree
+ * @return
+ *   the allocated configuration node, or NULL on error
+ */
+struct cfzy_confnode *cfzy_confnode_alloc(struct cfzy_conftree *conftree);
+
+/**
+ * Free the node given as argument, and all its associated data
+ *
+ * This function will free the children of the nodes, then the node
+ * itself, first by calling the specific function n->ops->free() if
+ * defined, then by doing the generic operations.
+ *
+ * @param n
+ *   configuration node
+ */
+void cfzy_confnode_free(struct cfzy_confnode *n);
+
+/**
+ * Get the prerequisites for this node
+ *
+ * Return the list of nodes required to evaluate the value of the
+ * given node. Indeed, each node has a 'expr_deps' field storing a
+ * list of expressions and a list of default value associated to
+ * expressions. This function returns the list of all nodes referenced
+ * in these expressions and all the ancestor nodes.
+ *
+ * The returned list must be freed by the user with
+ * cfzy_list_free(list, NULL).
+ *
+ * @param n
+ *   configuration node
+ * @return
+ *   list of nodes, or NULL on error
+ */
+struct cfzy_list_head *
+cfzy_confnode_get_deplist(const struct cfzy_confnode *n);
+
+/**
+ * Add a conditional default value to a node
+ *
+ * @param n
+ *   configuration node
+ * @param val
+ *   value to be used as default value
+ * @param expr_buf
+ *   condition string to enable the default value
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_confnode_add_defval(struct cfzy_confnode *n, const char *val,
+                            const char *expr_buf);
+
+/**
+ * Parse a line of configuration tree file, expecting an attribute
+ *
+ * This function first tries to match specific node attributes, using
+ * the specific operation n->ops->add_attr() if registered. Then it
+ * tries to match generic attributes (prompt, requires, default).
+ *
+ * This function is internal to the confizery library (called by the
+ * configuration tree parser).
+ *
+ * @param n
+ *   configuration node
+ * @param tklist
+ *   list of tokens for this line
+ * @return
+ *   SUCCESS if it matches an attribute of this node, NO_MATCH if the line
+ *   is not an attribute for this node, ERROR on error
+ */
+enum cfzy_parse_return
+cfzy_confnode_add_attr(struct cfzy_confnode *n,
+                      const struct cfzy_token_list *tklist);
+
+/**
+ * Write .config-like configuration in a file
+ *
+ * Write in a file the configuration values of this node and its
+ * descendants. The format of this file is like .config: it can be
+ * sourced by a shell or a Makefile.
+ *
+ * The function calls the specific node operation
+ * n->ops->dotconfig_write() if defined. Else, it uses the defaut
+ * behavior, which varies depending on node flags.
+ *
+ * This function is internal and is called by the public function
+ * cfzy_dotconfig_write().
+ *
+ * @param n
+ *   configuration node
+ * @param f
+ *   open file with write permissions where config will be written
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_confnode_dotconfig_write(const struct cfzy_confnode *n, FILE *f);
+
+/**
+ * Write the configuration in a C header file
+ *
+ * Write in a file the configuration values of this node and its
+ * descendants. The format of this file is a C header, so it can be
+ * included by a C application.
+ *
+ * The function calls the specific node operation
+ * n->ops->c_hdr_write() if defined. Else, it uses the defaut
+ * behavior, which varies depending on node flags.
+ *
+ * This function is internal and is called by the public function
+ * cfzy_c_hdr_write().
+ *
+ * @param n
+ *   configuration node
+ * @param f
+ *   open file with write permissions where config will be written
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_confnode_c_hdr_write(const struct cfzy_confnode *n, FILE *f);
+
+/**
+ * Return a string identifying the node type
+ *
+ * The function calls the specific node operation
+ * n->ops->get_type_str().
+ *
+ * The returned string points to static memory and contains the type
+ * of the node (ex: "config", "menuconfig", "choice", ...)
+ *
+ * @param n
+ *   configuration node
+ * @return
+ *   pointer to a node type string
+ */
+const char *cfzy_confnode_get_type_str(const struct cfzy_confnode *n);
+
+/**
+ * Return the boolean value of the node given the string value
+ *
+ * Convert the string value given as parameter to a boolean value. It
+ * calls the specific node operation n->ops->str2bool() if
+ * defined. Else, it uses the defaut behavior: return 0 for value=NULL
+ * or 1 for any other value.
+ *
+ * The specific function (if defined) can also return a negative value
+ * to indicate that the given value is not valid for this node.
+ *
+ * @param n
+ *   configuration node
+ * @param value
+ *   string containing the value
+ * @return
+ *   the boolean value (0 or 1) if the string value is valid,
+ *   else -1
+ */
+int cfzy_confnode_str2bool(const struct cfzy_confnode *n, const char *value);
+
+/**
+ * Get the path of the node
+ *
+ * @param n
+ *   configuration node
+ * @param buf
+ *   buffer where the path should be written
+ * @param len
+ *   length of the buffer
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_confnode_path(const struct cfzy_confnode *n, char *buf, int len);
+
+/* dump modes */
+#define CFZY_DUMP_MODE_MINIMAL        0x0
+#define CFZY_DUMP_MODE_STANDARD       0x1
+#define CFZY_DUMP_MODE_FULL           0x2
+#define CFZY_DUMP_MODE_FULL_RECURSIVE 0x3
+
+/**
+ * Dump a human-readable configuration in a file
+ *
+ * Write in a file the structure fields of this node and its
+ * descendants.
+ *
+ * @param n
+ *   configuration node
+ * @param f
+ *   open file with write permissions where configuration will be written
+ * @param mode
+ *   behavior of dump (CFZY_DUMP_MODE_MINIMAL, CFZY_DUMP_MODE_STANDARD, ...)
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_confnode_dump(const struct cfzy_confnode *n, FILE *f, int mode);
+
+/**
+ * Check prerequisites for a node
+ *
+ * @param n
+ *   configuration node to be checked
+ * @return
+ *  0 if a parent node is disabled or if a prerequisite expression is
+ *  false, 1 if node can be enable. On error, return -1.
+ */
+int cfzy_confnode_check_deps(struct cfzy_confnode *n);
+
+/**
+ * Evaluate the effective value of a node
+ *
+ * Set the new value of n->effective_value, assuming all prerequisites
+ * are already evaluated. This function is used as a callback by
+ * cfzy_expr_parse() when parsing an expression for
+ * cfzy_confnode_evaluate().
+ *
+ * @return n
+ *   configuration node
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_confnode_evaluate(struct cfzy_confnode *n);
+
+/**
+ * Get the boolean value of a node, given its name
+ *
+ * Return the effective value of a node (assuming it has been
+ * evaluated previously).
+ *
+ * @param name
+ *   configuration node name
+ * @param conftree
+ *   configuration tree
+ * @return
+ *   the boolean value (0 or 1) if the string value is valid,
+ *   else -1
+ */
+int cfzy_confnode_get_boolvalue(const char *name,
+                               const struct cfzy_conftree *conftree);
+
+/**
+ * Set user value of a configuration node
+ *
+ * To set the user value of a node, the user must not access to the
+ * field directly but use this function. Indeed, a node may want to do
+ * a specific operation (like a check, or modifying children nodes)
+ * stored in n->ops->set_uservalue().
+ *
+ * @return n
+ *   configuration node
+ * @param value
+ *   value to be set as user value
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_confnode_set_uservalue(struct cfzy_confnode *n, const char *value);
+
+/**
+ * Create a new root node
+ *
+ * @param conftree
+ *   configuration tree
+ * @return
+ *   a pointer to the configuration node, or NULL on error
+ */
+struct cfzy_confnode *cfzy_confnode_root_new(struct cfzy_conftree *conftree);
+
+/**
+ * Parse a line of configuration tree file, expecting new choice node
+ *
+ * @param n
+ *   configuration node
+ * @param tklist
+ *   list of tokens for this line
+ * @return
+ *   SUCCESS if it matches an attribute of this node, NO_MATCH if the line
+ *   is not an attribute for this node, ERROR on error
+ */
+enum cfzy_parse_return
+cfzy_confnode_choice_new(struct cfzy_confnode *n,
+                        const struct cfzy_token_list *tklist);
+/**
+ * Parse a line of configuration tree file, expecting new choiceconfig node
+ *
+ * @param n
+ *   configuration node
+ * @param tklist
+ *   list of tokens for this line
+ * @return
+ *   SUCCESS if it matches an attribute of this node, NO_MATCH if the line
+ *   is not an attribute for this node, ERROR on error
+ */
+enum cfzy_parse_return
+cfzy_confnode_choiceconfig_new(struct cfzy_confnode *n,
+                              const struct cfzy_token_list *tklist);
+
+/**
+ * Parse a line of configuration tree file, expecting new comment node
+ *
+ * @param n
+ *   configuration node
+ * @param tklist
+ *   list of tokens for this line
+ * @return
+ *   SUCCESS if it matches an attribute of this node, NO_MATCH if the line
+ *   is not an attribute for this node, ERROR on error
+ */
+enum cfzy_parse_return
+cfzy_confnode_comment_new(struct cfzy_confnode *n,
+                         const struct cfzy_token_list *tklist);
+
+/**
+ * Parse a line of configuration tree file, expecting new config node
+ *
+ * @param n
+ *   configuration node
+ * @param tklist
+ *   list of tokens for this line
+ * @return
+ *   SUCCESS if it matches an attribute of this node, NO_MATCH if the line
+ *   is not an attribute for this node, ERROR on error
+ */
+enum cfzy_parse_return
+cfzy_confnode_config_new(struct cfzy_confnode *n,
+                        const struct cfzy_token_list *tklist);
+
+/**
+ * Parse a line of configuration tree file, expecting new if node
+ *
+ * @param n
+ *   configuration node
+ * @param tklist
+ *   list of tokens for this line
+ * @return
+ *   SUCCESS if it matches an attribute of this node, NO_MATCH if the line
+ *   is not an attribute for this node, ERROR on error
+ */
+enum cfzy_parse_return
+cfzy_confnode_if_new(struct cfzy_confnode *n,
+                    const struct cfzy_token_list *tklist);
+
+/**
+ * Parse a line of configuration tree file, expecting new intconfig node
+ *
+ * @param n
+ *   configuration node
+ * @param tklist
+ *   list of tokens for this line
+ * @return
+ *   SUCCESS if it matches an attribute of this node, NO_MATCH if the line
+ *   is not an attribute for this node, ERROR on error
+ */
+enum cfzy_parse_return
+cfzy_confnode_intconfig_new(struct cfzy_confnode *n,
+                           const struct cfzy_token_list *tklist);
+
+/**
+ * Parse a line of configuration tree file, expecting new menu node
+ *
+ * @param n
+ *   configuration node
+ * @param tklist
+ *   list of tokens for this line
+ * @return
+ *   SUCCESS if it matches an attribute of this node, NO_MATCH if the line
+ *   is not an attribute for this node, ERROR on error
+ */
+enum cfzy_parse_return
+cfzy_confnode_menu_new(struct cfzy_confnode *n,
+                      const struct cfzy_token_list *tklist);
+
+/**
+ * Parse a line of configuration tree file, expecting new menuconfig node
+ *
+ * @param n
+ *   configuration node
+ * @param tklist
+ *   list of tokens for this line
+ * @return
+ *   SUCCESS if it matches an attribute of this node, NO_MATCH if the line
+ *   is not an attribute for this node, ERROR on error
+ */
+enum cfzy_parse_return
+cfzy_confnode_menuconfig_new(struct cfzy_confnode *n,
+                            const struct cfzy_token_list *tklist);
+
+/**
+ * Parse a line of configuration tree file, expecting new strconfig node
+ *
+ * @param n
+ *   configuration node
+ * @param tklist
+ *   list of tokens for this line
+ * @return
+ *   SUCCESS if it matches an attribute of this node, NO_MATCH if the line
+ *   is not an attribute for this node, ERROR on error
+ */
+enum cfzy_parse_return
+cfzy_confnode_strconfig_new(struct cfzy_confnode *n,
+                           const struct cfzy_token_list *tklist);
+
+/**
+ * Close the node beeing parsed
+ *
+ * Some nodes can contains other nodes (menu, menuconfig, ...). When
+ * the parser has finished to parse the descendants of the node, it
+ * calls this function to close the node. This can trigger some
+ * specific operations (like checks) if the node type registers a
+ * n->ops->close(). If no specific function is registered, it does
+ * nothing.
+ *
+ * This function is internal to the confizery library (called by the
+ * configuration tree parser).
+ *
+ * @param n
+ *   configuration node
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_confnode_close(struct cfzy_confnode *n);
+
+
+#endif /* _CFZY_CONFNODE_H_ */
diff --git a/tools/cfzy/libconfizery/cfzy_confnode_choice.c b/tools/cfzy/libconfizery/cfzy_confnode_choice.c
new file mode 100644 (file)
index 0000000..b564748
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2010, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "cfzy_log.h"
+#include "cfzy_list.h"
+#include "cfzy_expr.h"
+#include "cfzy_confnode.h"
+
+#define LOG(level, fmt, args...)                               \
+       CFZY_LOG("confnode_choice", level, fmt, ##args)
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *choice_get_type_str(const struct cfzy_confnode *n)
+{
+       (void)n;
+       return "choice";
+}
+
+static int choice_str2bool(const struct cfzy_confnode *n, const char *value)
+{
+       struct cfzy_confnode *c;
+
+       /* NULL means "disabled" */
+       if (value == NULL)
+               return 0;
+
+       /* if we have no children, it's because we are creating the
+        * node, so accept all values, we will check them later in
+        * choice_close() */
+       if (TAILQ_EMPTY(&n->children))
+               return 1;
+
+       /* else, the value must be the name of a children */
+       TAILQ_FOREACH(c, &n->children, child_next) {
+               if (!strcmp(value, c->name))
+                       return 1;
+       }
+
+       /* invalid value */
+       return -1;
+}
+
+static int choice_close(struct cfzy_confnode *n)
+{
+       struct cfzy_confnode_defvalue *defval;
+       struct cfzy_list_elt *e;
+       struct cfzy_confnode *c;
+       char exprbuf[512];
+       int ret;
+
+       /* browse all default values and call choice_str2bool() on
+        * them to check that they are correct */
+       TAILQ_FOREACH(e, n->default_val_list, next) {
+               defval = e->ptr;
+
+               if (choice_str2bool(n, defval->val) < 0) {
+                       LOG(ERR, "invalid default value <%s> for node <%s>\n",
+                           defval->val, n->name);
+                       return -1;
+               }
+       }
+       if (choice_str2bool(n, n->default_value) < 0) {
+               LOG(ERR, "invalid default value <%s> for node <%s>\n",
+                   n->default_value, n->name);
+               return -1;
+       }
+
+       /* for each child node */
+       TAILQ_FOREACH(c, &n->children, child_next) {
+
+               /* set default value */
+               if (!strcmp(n->default_value, c->name))
+                       c->default_value = strdup("y");
+               else
+                       c->default_value = strdup("n");
+
+               if (c->default_value == NULL) {
+                       LOG(ERR, "cannot allocate default value>\n");
+                       return -1;
+               }
+
+               /* set conditional default values for child nodes with
+                * the same condition */
+               TAILQ_FOREACH(e, n->default_val_list, next) {
+                       defval = e->ptr;
+
+                       if (cfzy_expr_to_str(defval->expr, exprbuf,
+                                            sizeof(exprbuf)) < 0) {
+                               LOG(ERR, "cannot convert expression to str\n");
+                               return -1;
+                       }
+
+                       if (!strcmp(defval->val, c->name))
+                               ret = cfzy_confnode_add_defval(c, "y", exprbuf);
+                       else
+                               ret = cfzy_confnode_add_defval(c, "n", exprbuf);
+
+                       if (ret < 0) {
+                               LOG(ERR, "cannot add conditional default\n");
+                               return -1;
+                       }
+               }
+
+       }
+
+       return 0;
+}
+
+static int choice_set_uservalue(struct cfzy_confnode *n, const char *value)
+{
+       struct cfzy_confnode *c;
+       char *newvalue;
+
+       /* if value is NULL, unset this node and all its children,
+        * falling back to default value */
+       if (value == NULL) {
+               /* this node */
+               if (n->user_value != NULL)
+                       free(n->user_value);
+               n->user_value = NULL;
+
+               /* children */
+               TAILQ_FOREACH(c, &n->children, child_next) {
+                       if (c->user_value != NULL)
+                               free(c->user_value);
+                       c->user_value = NULL;
+               }
+               return 0;
+       }
+
+       /* if not NULL, the value must be the name of a children */
+       TAILQ_FOREACH(c, &n->children, child_next) {
+               if (!strcmp(value, c->name))
+                       break;
+       }
+       if (c == NULL)
+               return -1;
+
+
+       /* this node */
+       if (n->user_value != NULL)
+               free(n->user_value);
+       n->user_value = strdup(c->name);
+       if (n->user_value == NULL) {
+               LOG(ERR, "cannot allocate value\n");
+               return -1;
+       }
+
+       /* children */
+       TAILQ_FOREACH(c, &n->children, child_next) {
+               if (!strcmp(value, c->name))
+                       newvalue = strdup("y");
+               else
+                       newvalue = strdup("n");
+
+               if (newvalue == NULL) {
+                       LOG(ERR, "cannot allocate value\n");
+                       return -1;
+               }
+
+               if (c->user_value != NULL)
+                       free(c->user_value);
+               c->user_value = newvalue;
+       }
+
+       return 0;
+}
+
+static struct cfzy_confnode_ops choice_ops = {
+       .free = NULL,
+       .add_attr = NULL,
+       .close = choice_close,
+       .dotconfig_write = NULL,
+       .c_hdr_write = NULL,
+       .str2bool = choice_str2bool,
+       .get_type_str = choice_get_type_str,
+       .set_uservalue = choice_set_uservalue,
+};
+
+/* Create a new node */
+enum cfzy_parse_return cfzy_confnode_choice_new(struct cfzy_confnode *n,
+                                               const struct cfzy_token_list *tklist)
+{
+       struct cfzy_token *tok;
+       tok = TAILQ_FIRST(&tklist->list);
+
+       if (strcmp(tok->str, "choice") || tklist->n_token != 2)
+               return NO_MATCH;
+
+       tok = TAILQ_NEXT(tok, next);
+       n->ops = &choice_ops;
+       n->flags = CFZY_F_IS_DIR | CFZY_F_QUOTE_VAL;
+
+       n->name = strdup(tok->str);
+       if (n->name == NULL)
+               return ERROR;
+
+       return SUCCESS;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_confnode_choiceconfig.c b/tools/cfzy/libconfizery/cfzy_confnode_choiceconfig.c
new file mode 100644 (file)
index 0000000..ea01c17
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2010, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "cfzy_log.h"
+#include "cfzy_expr.h"
+#include "cfzy_confnode.h"
+
+#define LOG(level, fmt, args...)                               \
+       CFZY_LOG("confnode_choiceconfig", level, fmt, ##args)
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *choiceconfig_get_type_str(const struct cfzy_confnode *n)
+{
+       (void)n;
+       return "choiceconfig";
+}
+
+static int choiceconfig_str2bool(const struct cfzy_confnode *n,
+                                const char *value)
+{
+       (void)n;
+
+       /* NULL or "n" means disabled */
+       if (value == NULL || !strcmp("n", value))
+               return 0;
+
+       if (!strcmp("y", value))
+               return 1;
+
+       return -1;
+}
+
+static int choiceconfig_set_uservalue(struct cfzy_confnode *n,
+                                     const char *value)
+{
+       struct cfzy_confnode *c;
+
+       if (value == NULL)
+               return cfzy_confnode_set_uservalue(n->parent, NULL);
+
+       if (!strcmp(value, "y"))
+               return cfzy_confnode_set_uservalue(n->parent, n->name);
+
+       if (!strcmp(value, "n")) {
+               /* node is already set to "n", do nothing so we won't
+                * change the selected choice */
+               if (!strcmp(n->user_value, "n"))
+                       return 0;
+
+               /* else just set to 'y' the first brother that we find */
+               TAILQ_FOREACH(c, &n->children, child_next) {
+                       if (c != n)
+                               break;
+               }
+
+               /* no brother found */
+               if (c == NULL)
+                       return -1;
+
+               return cfzy_confnode_set_uservalue(c, "y");
+       }
+
+       return -1;
+}
+
+static struct cfzy_confnode_ops choiceconfig_ops = {
+       .free = NULL,
+       .add_attr = NULL,
+       .close = NULL,
+       .str2bool = choiceconfig_str2bool,
+       .dotconfig_write = NULL,
+       .c_hdr_write = NULL,
+       .get_type_str = choiceconfig_get_type_str,
+       .set_uservalue = choiceconfig_set_uservalue,
+};
+
+/* Create a new node */
+enum cfzy_parse_return
+cfzy_confnode_choiceconfig_new(struct cfzy_confnode *n,
+                              const struct cfzy_token_list *tklist)
+{
+       struct cfzy_token *tok;
+
+       tok = TAILQ_FIRST(&tklist->list);
+
+       if (strcmp(tok->str, "choiceconfig") || tklist->n_token != 2)
+               return NO_MATCH;
+
+       tok = TAILQ_NEXT(tok, next);
+       n->ops = &choiceconfig_ops;
+       n->flags = CFZY_F_NO_SET_DEFAULT | CFZY_F_VAL_IS_BOOL;
+
+       n->name = strdup(tok->str);
+       if (n->name == NULL)
+               return ERROR;
+
+       return SUCCESS;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_confnode_comment.c b/tools/cfzy/libconfizery/cfzy_confnode_comment.c
new file mode 100644 (file)
index 0000000..4e1e915
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2010, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "cfzy_log.h"
+#include "cfzy_confnode.h"
+
+#define LOG(level, fmt, args...)                               \
+       CFZY_LOG("comment", level, fmt, ##args)
+
+/* write config value in file f in a dotconfig-like manner. Return 0
+ * on success. */
+static int comment_dotconfig_write(const struct cfzy_confnode *n, FILE *f)
+{
+       if (fprintf(f, "# %s\n", n->prompt) < 0)
+               return -1;
+
+       /* this node has no children, no need to dump them */
+       return 0;
+}
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *comment_get_type_str(const struct cfzy_confnode *n)
+{
+       (void)n;
+       return "comment";
+}
+
+static struct cfzy_confnode_ops comment_ops = {
+       .free = NULL,
+       .add_attr = NULL,
+       .close = NULL,
+       .str2bool = NULL,
+       .dotconfig_write = comment_dotconfig_write,
+       .c_hdr_write = NULL,
+       .get_type_str = comment_get_type_str,
+       .set_uservalue = NULL,
+};
+
+/* Create a new node */
+enum cfzy_parse_return cfzy_confnode_comment_new(struct cfzy_confnode *n,
+                                                const struct cfzy_token_list *tklist)
+{
+       struct cfzy_token *tok;
+       tok = TAILQ_FIRST(&tklist->list);
+
+       if (strcmp(tok->str, "comment") || tklist->n_token != 2)
+               return NO_MATCH;
+
+       tok = TAILQ_NEXT(tok, next);
+       /* comment string is stored in prompt field, not in name */
+       if (n->prompt != NULL) {
+               free(n->prompt);
+               n->prompt = NULL;
+       }
+       n->prompt = strdup(tok->str);
+       if (n->prompt == NULL) {
+               LOG(ERR, "cannot allocate prompt");
+               return ERROR;
+       }
+       n->ops = &comment_ops;
+       n->flags = CFZY_F_NO_SET_DEFAULT |
+               CFZY_F_NO_SET_PROMPT |
+               CFZY_F_NO_NAME |
+               CFZY_F_NO_SET_VALUE;
+
+       return SUCCESS;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_confnode_config.c b/tools/cfzy/libconfizery/cfzy_confnode_config.c
new file mode 100644 (file)
index 0000000..7b802ef
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2010, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "cfzy_confnode.h"
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *config_get_type_str(const struct cfzy_confnode *n)
+{
+       (void)n;
+       return "config";
+}
+
+static int config_str2bool(const struct cfzy_confnode *n,
+                                    const char *value)
+{
+       (void)n;
+
+       /* NULL or "n" means disabled */
+       if (value == NULL || !strcmp("n", value))
+               return 0;
+
+       if (!strcmp("y", value))
+               return 1;
+
+       return -1;
+}
+
+static struct cfzy_confnode_ops config_ops = {
+       .free = NULL,
+       .add_attr = NULL,
+       .close = NULL,
+       .str2bool = config_str2bool,
+       .dotconfig_write = NULL,
+       .c_hdr_write = NULL,
+       .get_type_str = config_get_type_str,
+       .set_uservalue = NULL,
+};
+
+/* Create a new node */
+enum cfzy_parse_return cfzy_confnode_config_new(struct cfzy_confnode *n,
+                                               const struct cfzy_token_list *tklist)
+{
+       struct cfzy_token *tok;
+       tok = TAILQ_FIRST(&tklist->list);
+
+       if (strcmp(tok->str, "config") || tklist->n_token != 2)
+               return NO_MATCH;
+
+       tok = TAILQ_NEXT(tok, next);
+       n->ops = &config_ops;
+       n->flags = CFZY_F_VAL_IS_BOOL;
+
+       n->name = strdup(tok->str);
+       if (n->name == NULL)
+               return ERROR;
+
+       n->default_value = strdup("n");
+       if (n->default_value == NULL) {
+               free(n->name);
+               n->name = NULL;
+               return ERROR;
+       }
+
+       return SUCCESS;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_confnode_if.c b/tools/cfzy/libconfizery/cfzy_confnode_if.c
new file mode 100644 (file)
index 0000000..2fd68b3
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2010, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "cfzy_list.h"
+#include "cfzy_expr.h"
+#include "cfzy_confnode.h"
+
+/* write config value in file f in a dotconfig-like manner. Return -1
+ * on error. */
+static int if_dotconfig_write(const struct cfzy_confnode *n, FILE *f)
+{
+       const struct cfzy_confnode *c;
+       int val;
+
+       val = cfzy_confnode_str2bool(n, n->effective_value);
+       if (val < 0)
+               return -1;
+
+       /* condition is wrong, nothing to dump */
+       if (val == 0)
+               return 0;
+
+       /* else, just dump children */
+       TAILQ_FOREACH(c, &n->children, child_next) {
+               if (cfzy_confnode_dotconfig_write(c, f) < 0)
+                       return -1;
+       }
+       return 0;
+}
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *if_get_type_str(const struct cfzy_confnode *n)
+{
+       (void)n;
+       return "if";
+}
+
+/* evaluate the expression stored in the node */
+static int if_str2bool(const struct cfzy_confnode *n, const char *value)
+{
+       struct cfzy_list_elt *e;
+       struct cfzy_expr *expr;
+       int (*get_boolvalue)(const char *, const struct cfzy_conftree *);
+       int (*get_boolvalue_casted)(const char *, void *);
+
+       (void)n;
+
+       /* should not happen */
+       if (value != NULL)
+               return -1;
+
+       e = TAILQ_FIRST(n->expr_deps);
+       if (e == NULL)
+               return -1;
+       expr = e->ptr;
+
+       /* we use an intermediate variable to have a compiler warning
+        * if the prototype of cfzy_confnode_get_boolvalue changes. We
+        * know that that these types are compatible here. */
+       get_boolvalue = cfzy_confnode_get_boolvalue;
+       get_boolvalue_casted = (void *)get_boolvalue;
+
+       return cfzy_expr_eval(expr, get_boolvalue_casted, n->conftree);
+}
+
+static struct cfzy_confnode_ops if_ops = {
+       .free = NULL,
+       .add_attr = NULL,
+       .close = NULL,
+       .str2bool = if_str2bool,
+       .dotconfig_write = if_dotconfig_write,
+       .c_hdr_write = NULL,
+       .get_type_str = if_get_type_str,
+       .set_uservalue = NULL,
+};
+
+/* Create a new node */
+enum cfzy_parse_return cfzy_confnode_if_new(struct cfzy_confnode *n,
+                                           const struct cfzy_token_list *tklist)
+{
+       struct cfzy_token *tok;
+       struct cfzy_expr *exp;
+
+       tok = TAILQ_FIRST(&tklist->list);
+
+       if (strcmp(tok->str, "if") || tklist->n_token < 2)
+               return NO_MATCH;
+
+       tok = TAILQ_NEXT(tok, next); /* points to expression */
+       exp = cfzy_expr_parse(tklist->linebuf + tok->offset);
+       if (exp == NULL)
+               return ERROR;
+
+       if (cfzy_list_add_tail(n->expr_deps, exp) < 0) {
+               cfzy_expr_free(exp);
+               return ERROR;
+       }
+
+       n->ops = &if_ops;
+       n->flags = CFZY_F_INVISIBLE |
+               CFZY_F_NO_NAME |
+               CFZY_F_NO_SET_DEPS |
+               CFZY_F_NO_SET_PROMPT |
+               CFZY_F_NO_SET_DEFAULT |
+               CFZY_F_NO_SET_VALUE |
+               CFZY_F_IS_DIR;
+
+       return SUCCESS;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_confnode_intconfig.c b/tools/cfzy/libconfizery/cfzy_confnode_intconfig.c
new file mode 100644 (file)
index 0000000..affd57f
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2010, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "cfzy_confnode.h"
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *intconfig_get_type_str(const struct cfzy_confnode *n)
+{
+       (void)n;
+       return "intconfig";
+}
+
+static int intconfig_str2bool(const struct cfzy_confnode *n,
+                                   const char *value)
+{
+       char *end;
+       int64_t val;
+
+       (void)n;
+
+       /* NULL means disabled */
+       if (value == NULL)
+               return 0;
+
+       val = strtoll(value, &end, 0);
+       if (*end != 0)
+               return -1;
+
+       /* XXX check range attr here */
+
+       return val != 0;
+}
+
+static struct cfzy_confnode_ops intconfig_ops = {
+       .free = NULL,
+       .add_attr = NULL, /* XXX range, displayhex */
+       .close = NULL,
+       .str2bool = intconfig_str2bool,
+       .dotconfig_write = NULL,
+       .c_hdr_write = NULL,
+       .get_type_str = intconfig_get_type_str,
+       .set_uservalue = NULL,
+};
+
+/* Create a new node */
+enum cfzy_parse_return cfzy_confnode_intconfig_new(struct cfzy_confnode *n,
+                                                  const struct cfzy_token_list *tklist)
+{
+       struct cfzy_token *tok;
+       tok = TAILQ_FIRST(&tklist->list);
+
+       if (strcmp(tok->str, "intconfig") || tklist->n_token != 2)
+               return NO_MATCH;
+
+       tok = TAILQ_NEXT(tok, next);
+       n->ops = &intconfig_ops;
+       n->flags = 0;
+
+       n->name = strdup(tok->str);
+       if (n->name == NULL)
+               return ERROR;
+
+       n->default_value = strdup("0");
+       if (n->default_value == NULL) {
+               free(n->name);
+               n->name = NULL;
+               return ERROR;
+       }
+
+       return SUCCESS;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_confnode_menu.c b/tools/cfzy/libconfizery/cfzy_confnode_menu.c
new file mode 100644 (file)
index 0000000..1d8c6ce
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2010, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "cfzy_confnode.h"
+
+/* write config value in file f in a dotconfig-like manner. Return 0
+ * on success. */
+static int menu_dotconfig_write(const struct cfzy_confnode *n, FILE *f)
+{
+       const struct cfzy_confnode *c;
+
+       if (fprintf(f,
+                   "#\n"
+                   "# -- %s\n"
+                   "#\n", n->prompt) < 0)
+               return -1;
+
+       /* dump children */
+       TAILQ_FOREACH(c, &n->children, child_next) {
+               if (cfzy_confnode_dotconfig_write(c, f) < 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+/* this node is always enabled whatever the value */
+static int menu_str2bool(const struct cfzy_confnode *n,
+                             const char *value)
+{
+       (void)n;
+
+       /* should not happen */
+       if (value != NULL)
+               return -1;
+
+       return 1;
+}
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *menu_get_type_str(const struct cfzy_confnode *n)
+{
+       (void)n;
+       return "menu";
+}
+
+static struct cfzy_confnode_ops menu_ops = {
+       .free = NULL,
+       .add_attr = NULL,
+       .close = NULL,
+       .str2bool = menu_str2bool,
+       .dotconfig_write = menu_dotconfig_write,
+       .c_hdr_write = NULL,
+       .get_type_str = menu_get_type_str,
+       .set_uservalue = NULL,
+};
+
+/* Create a new node */
+enum cfzy_parse_return cfzy_confnode_menu_new(struct cfzy_confnode *n,
+                                             const struct cfzy_token_list *tklist)
+{
+       struct cfzy_token *tok;
+       tok = TAILQ_FIRST(&tklist->list);
+
+       if (strcmp(tok->str, "menu") || tklist->n_token != 2)
+               return NO_MATCH;
+
+       tok = TAILQ_NEXT(tok, next);
+       n->ops = &menu_ops;
+       n->flags = CFZY_F_IS_DIR |
+               CFZY_F_NO_SET_DEPS |
+               CFZY_F_NO_SET_DEFAULT |
+               CFZY_F_NO_SET_VALUE |
+               CFZY_F_DOTCONF_SHOW_TITLE;
+
+       n->name = strdup(tok->str);
+       if (n->name == NULL)
+               return ERROR;
+
+       return SUCCESS;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_confnode_menuconfig.c b/tools/cfzy/libconfizery/cfzy_confnode_menuconfig.c
new file mode 100644 (file)
index 0000000..73af69e
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2010, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "cfzy_confnode.h"
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *menuconfig_get_type_str(const struct cfzy_confnode *n)
+{
+       (void)n;
+       return "menuconfig";
+}
+
+static int menuconfig_str2bool(const struct cfzy_confnode *n,
+                                    const char *value)
+{
+       (void)n;
+
+       if (!strcmp("y", value))
+               return 1;
+
+       if (!strcmp("n", value))
+               return 0;
+
+       return -1;
+}
+
+static struct cfzy_confnode_ops menuconfig_ops = {
+       .free = NULL,
+       .add_attr = NULL,
+       .close = NULL,
+       .str2bool = menuconfig_str2bool,
+       .dotconfig_write = NULL,
+       .c_hdr_write = NULL,
+       .get_type_str = menuconfig_get_type_str,
+       .set_uservalue = NULL,
+};
+
+/* Create a new node */
+enum cfzy_parse_return
+cfzy_confnode_menuconfig_new(struct cfzy_confnode *n,
+                            const struct cfzy_token_list *tklist)
+{
+       struct cfzy_token *tok;
+       tok = TAILQ_FIRST(&tklist->list);
+
+       if (strcmp(tok->str, "menuconfig") || tklist->n_token != 2)
+               return NO_MATCH;
+
+       tok = TAILQ_NEXT(tok, next);
+       n->ops = &menuconfig_ops;
+       n->flags = CFZY_F_IS_DIR | CFZY_F_DOTCONF_SHOW_TITLE |
+               CFZY_F_VAL_IS_BOOL;
+
+       n->name = strdup(tok->str);
+       if (n->name == NULL)
+               return ERROR;
+
+       n->default_value = strdup("n");
+       if (n->default_value == NULL) {
+               free(n->name);
+               n->name = NULL;
+               return ERROR;
+       }
+
+       return SUCCESS;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_confnode_ops.h b/tools/cfzy/libconfizery/cfzy_confnode_ops.h
new file mode 100644 (file)
index 0000000..68ce24e
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * Confizery configuration node specific operations
+ *
+ * This file describes specific node operations, called internally by
+ * cfzy_confnode module.
+ */
+
+#ifndef _CFZY_CONFNODE_OPS_H_
+#define _CFZY_CONFNODE_OPS_H_
+
+#include <stdio.h>
+
+#include "cfzy_conftree_parser.h"
+
+struct cfzy_confnode;
+struct cfzy_token_list;
+
+/**
+ * Node operation to free the specific node data. Called by
+ * cfzy_confnode_free().
+ */
+typedef void (confnode_free_t)(struct cfzy_confnode *n);
+
+/**
+ * Specific node operation to parse a conftree line, expecting a new
+ * attribute. Called by cfzy_confnode_add_attr().
+ */
+typedef enum cfzy_parse_return
+(confnode_add_attr_t)(struct cfzy_confnode *n,
+                     const struct cfzy_token_list *tklist,
+                     const char *linebuf);
+
+/**
+ * Specific node operation to close a node beeing parsed. Called by
+ * cfzy_confnode_close().
+ */
+typedef int (confnode_close_t)(struct cfzy_confnode *n);
+
+/**
+ * Specific node operation that writes the configuration in a
+ * file. Called by cfzy_confnode_dotconfig_write().
+ */
+typedef int (confnode_dotconfig_write_t)(const struct cfzy_confnode *n, FILE *f);
+
+/**
+ * Specific node operation that writes the configuration in a C header
+ * file. Called by cfzy_confnode_c_hdr_write().
+ */
+typedef int (confnode_c_hdr_write_t)(const struct cfzy_confnode *n, FILE *f);
+
+/**
+ * Specific node operation that returns the boolean value of the node
+ * given the string value. Called by cfzy_confnode_str2bool().
+ */
+typedef int (confnode_str2bool_t)(const struct cfzy_confnode *n,
+                                 const char *strvalue);
+
+/**
+ * Specific node operation that returns a string identifying the node
+ * type. Called by cfzy_confnode_get_type_str().
+ */
+typedef const char *(confnode_get_type_str_t)(const struct cfzy_confnode *n);
+
+/**
+ * Specific node operation to set user value of a configuration
+ * node. Called by cfzy_confnode_set_uservalue().
+ */
+typedef int (confnode_set_uservalue_t)(struct cfzy_confnode *n,
+                                      const char *strvalue);
+
+/* all node-specific operations */
+struct cfzy_confnode_ops {
+       confnode_free_t *free;                 /**< free specific node data */
+       confnode_add_attr_t *add_attr;         /**< try to parse specific attr */
+       confnode_close_t *close;               /**< close node beeing parsed */
+       confnode_dotconfig_write_t *dotconfig_write; /**< write in dotconfig */
+       confnode_c_hdr_write_t *c_hdr_write;   /**< write in C header file */
+       confnode_str2bool_t *str2bool;         /**< check strvalue and
+                                                 return boolvalue */
+       confnode_get_type_str_t *get_type_str; /**< return type string */
+       confnode_set_uservalue_t *set_uservalue; /**< check and set user val */
+};
+
+#endif /* _CFZY_CONFNODE_OPS_H_ */
diff --git a/tools/cfzy/libconfizery/cfzy_confnode_root.c b/tools/cfzy/libconfizery/cfzy_confnode_root.c
new file mode 100644 (file)
index 0000000..86feb82
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2010, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "cfzy_confnode.h"
+
+/* this node is always enabled whatever the value */
+static int root_str2bool(const struct cfzy_confnode *n,
+                             const char *value)
+{
+       (void)n;
+
+       /* should not happen */
+       if (value != NULL)
+               return -1;
+
+       return 1;
+}
+
+static struct cfzy_confnode_ops root_ops = {
+       .free = NULL,
+       .add_attr = NULL,
+       .close = NULL,
+       .str2bool = root_str2bool,
+       .c_hdr_write = NULL,
+       .dotconfig_write = NULL,
+       .get_type_str = NULL,
+       .set_uservalue = NULL,
+};
+
+/* Create a new root node */
+struct cfzy_confnode *cfzy_confnode_root_new(struct cfzy_conftree *conftree)
+{
+       struct cfzy_confnode *n;
+
+       n = cfzy_confnode_alloc(conftree);
+       if (n == NULL)
+               return NULL;
+
+       n->ops = &root_ops;
+       n->flags = CFZY_F_IS_ROOT |
+               CFZY_F_INVISIBLE |
+               CFZY_F_NO_SET_VALUE |
+               CFZY_F_NO_NAME |
+               CFZY_F_NO_SET_DEFAULT |
+               CFZY_F_NO_SET_DEPS;
+
+       n->name = strdup("root");
+       if (n->name == NULL)
+               return NULL;
+
+       return n;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_confnode_strconfig.c b/tools/cfzy/libconfizery/cfzy_confnode_strconfig.c
new file mode 100644 (file)
index 0000000..1d0ea31
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2010, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "cfzy_confnode.h"
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *strconfig_get_type_str(const struct cfzy_confnode *n)
+{
+       (void)n;
+       return "strconfig";
+}
+
+static int strconfig_str2bool(const struct cfzy_confnode *n,
+                                   const char *value)
+{
+       (void)n;
+
+       if (value == NULL || !strcmp("", value))
+               return 0;
+
+       return 1;
+}
+
+static struct cfzy_confnode_ops strconfig_ops = {
+       .free = NULL,
+       .add_attr = NULL,
+       .close = NULL,
+       .str2bool = strconfig_str2bool,
+       .dotconfig_write = NULL,
+       .c_hdr_write = NULL,
+       .get_type_str = strconfig_get_type_str,
+       .set_uservalue = NULL,
+};
+
+/* Create a new node */
+enum cfzy_parse_return
+cfzy_confnode_strconfig_new(struct cfzy_confnode *n,
+                           const struct cfzy_token_list *tklist)
+{
+       struct cfzy_token *tok;
+       tok = TAILQ_FIRST(&tklist->list);
+
+       if (strcmp(tok->str, "strconfig") || tklist->n_token != 2)
+               return NO_MATCH;
+
+       tok = TAILQ_NEXT(tok, next);
+       n->ops = &strconfig_ops;
+       n->flags = CFZY_F_QUOTE_VAL;
+
+       n->name = strdup(tok->str);
+       if (n->name == NULL)
+               return ERROR;
+
+       n->default_value = strdup("");
+       if (n->default_value == NULL) {
+               free(n->name);
+               n->name = NULL;
+               return ERROR;
+       }
+
+       return SUCCESS;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_conftree.c b/tools/cfzy/libconfizery/cfzy_conftree.c
new file mode 100644 (file)
index 0000000..b4c7777
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "cfzy_log.h"
+#include "cfzy_list.h"
+#include "cfzy_htable.h"
+#include "cfzy_confnode.h"
+#include "cfzy_conftree.h"
+#include "cfzy_conftree_parser.h"
+
+#define LOG(level, fmt, args...)                               \
+       CFZY_LOG("conftree", level, fmt, ##args)
+
+/* Parse a configuration tree and add all the nodes in the
+ * hashtable. Return the number of nodes or -1 on error. */
+static int conftree_fill_htable(struct cfzy_conftree *conftree,
+                               struct cfzy_confnode *n)
+{
+       struct cfzy_confnode *c;
+       int ret, count;
+
+       if (n->name != NULL) {
+               if (cfzy_htable_lookup(conftree->nodes_htable, n->name) != NULL) {
+                       LOG(ERR, "duplicate symbol <%s>\n", n->name);
+                       return -1;
+               }
+
+               if (cfzy_htable_add(conftree->nodes_htable, n->name, n) < 0)
+                       return -1;
+       }
+       count = 1; /* count the nodes recursively */
+
+       TAILQ_FOREACH(c, &n->children, child_next) {
+               ret = conftree_fill_htable(conftree, c);
+               if (ret < 0)
+                       return -1;
+               count += ret;
+       }
+
+       return count;
+}
+
+/* Parse a configuration tree and add all the nodes in the hashtable */
+static int conftree_generate_deplist(struct cfzy_confnode *n)
+{
+       struct cfzy_confnode *c;
+
+       n->node_deps = cfzy_confnode_get_deplist(n);
+       if (n->node_deps == NULL)
+               return -1;
+
+       TAILQ_FOREACH(c, &n->children, child_next) {
+               if (conftree_generate_deplist(c) < 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+/* Parse a configuration tree and add all the nodes in the
+ * hashtable. Return the number of nodes in the prio_list. */
+static int conftree_fill_prio_list(struct cfzy_conftree *conftree,
+                                  struct cfzy_confnode *n)
+{
+       struct cfzy_list_elt *e;
+       struct cfzy_confnode *c;
+       int ok = 1;
+       int count = 0, ret;
+
+       if (n->in_prio_list == 1) {
+               count = 1;
+       }
+       else {
+               /* if the node is not in prio_list, check if all
+                * prerequisites are in prio list, and add in the
+                * list */
+               TAILQ_FOREACH(e, n->node_deps, next) {
+                       c = e->ptr;
+                       if (c->in_prio_list == 0) {
+                               ok = 0;
+                               break;
+                       }
+               }
+               if (ok == 1) {
+                       TAILQ_INSERT_TAIL(&conftree->prio_list, n, prio_next);
+                       count = 1;
+                       n->in_prio_list = 1;
+               }
+       }
+
+       /* process children */
+       TAILQ_FOREACH(c, &n->children, child_next) {
+               ret = conftree_fill_prio_list(conftree, c);
+               if (ret < 0)
+                       return -1;
+               count += ret;
+       }
+
+       return count;
+}
+
+/* create a new configuration tree from filename */
+struct cfzy_conftree *cfzy_conftree_new(const char *filename)
+{
+       struct cfzy_conftree *conftree;
+       int node_count, prio_count, prev;
+
+       LOG(INFO, "Open conftree <%s>\n", filename);
+
+       conftree = malloc(sizeof(*conftree));
+       if (conftree == NULL)
+               return NULL;
+       memset(conftree, 0, sizeof(*conftree));
+
+       conftree->path = strdup(filename);
+       if (conftree->path == NULL) {
+               cfzy_conftree_free(conftree);
+               return NULL;
+       }
+
+       conftree->root = cfzy_confnode_root_new(conftree);
+       if (conftree->root == NULL) {
+               cfzy_conftree_free(conftree);
+               return NULL;
+       }
+
+       /* parse the configuration tree file */
+       if (cfzy_conftree_parse(filename, conftree) < 0) {
+               LOG(ERR, "cannot parse configuration tree\n");
+               cfzy_conftree_free(conftree);
+               return NULL;
+       }
+
+       conftree->nodes_htable = cfzy_htable_alloc();
+       if (conftree->nodes_htable == NULL) {
+               LOG(ERR, "cannot allocate conftree htable\n");
+               cfzy_conftree_free(conftree);
+               return NULL;
+       }
+
+       /* fill a htable indexed by node name */
+       node_count = conftree_fill_htable(conftree, conftree->root);
+       if (node_count < 0) {
+               LOG(ERR, "cannot fill conftree htable\n");
+               cfzy_conftree_free(conftree);
+               return NULL;
+       }
+       conftree->count = node_count;
+
+       /* for each node, generate the list of prerequisite nodes */
+       if (conftree_generate_deplist(conftree->root) < 0) {
+               LOG(ERR, "cannot generate dep list\n");
+               cfzy_conftree_free(conftree);
+               return NULL;
+       }
+
+       /* generate a list ordered by node priority: if Node1 depends
+        * on Node2, then Node1 is located after Node2 in the list. */
+       TAILQ_INIT(&conftree->prio_list);
+       prev = 0;
+       while (1) {
+               prio_count = conftree_fill_prio_list(conftree, conftree->root);
+               if (prio_count < 0) {
+                       LOG(ERR, "cannot generate prio list\n");
+                       cfzy_conftree_free(conftree);
+                       return NULL;
+               }
+               if (prio_count == node_count)
+                       break;
+               if (prio_count == prev) {
+                       LOG(ERR, "circular dependency in conf tree\n");
+                       cfzy_conftree_free(conftree);
+                       return NULL;
+               }
+               prev = prio_count;
+       }
+
+       return conftree;
+}
+
+/* free a configuration tree */
+void cfzy_conftree_free(struct cfzy_conftree *conftree)
+{
+       if (conftree->path != NULL)
+               free(conftree->path);
+       if (conftree->nodes_htable != NULL)
+               cfzy_htable_free(conftree->nodes_htable, NULL);
+       if (conftree->root != NULL)
+               cfzy_confnode_free(conftree->root);
+       free(conftree);
+}
+
+int cfzy_conftree_dump(const struct cfzy_conftree *conftree,
+                       const char *filename)
+{
+       FILE *f;
+
+       f = fopen(filename, "w");
+       if (f == NULL) {
+               LOG(ERR, "Cannot open <%s>: %s\n", filename, strerror(errno));
+               return -1;
+       }
+
+       if (cfzy_confnode_dump(conftree->root, f,
+                              CFZY_DUMP_MODE_FULL_RECURSIVE) < 0) {
+               LOG(ERR, "Cannot dump confnode tree\n");
+               fclose(f);
+               return -1;
+       }
+
+       fclose(f);
+       return 0;
+}
+
+int cfzy_conftree_evaluate(struct cfzy_conftree *conftree)
+{
+       struct cfzy_confnode *n;
+
+       TAILQ_FOREACH(n, &conftree->prio_list, prio_next) {
+               if (cfzy_confnode_evaluate(n) < 0) {
+                       LOG(ERR, "Cannot evaluate conftree\n");
+                       return -1;
+               }
+               LOG(DEBUG, "eval %s -> %s\n", n->name, n->effective_value);
+       }
+
+       return 0;
+}
+
+/* same than strcmp() but allow string to be NULL */
+static int strcmp2(const char *s1, const char *s2)
+{
+       if (s1 == NULL && s2 == NULL)
+               return 0;
+       if (s1 == NULL)
+               return -1;
+       if (s2 == NULL)
+               return 1;
+       return strcmp(s1, s2);
+}
+
+static int __filter(struct cfzy_confnode *n, struct cfzy_list_head *node_list,
+                   int flags)
+{
+       int add_it = 0;
+       struct cfzy_confnode *c;
+
+       if ((flags & CFZY_FILTER_F_USR_UNSET) && n->user_value == NULL)
+               add_it = 1;
+       else if ((flags & CFZY_FILTER_F_EFF_UNSET) && n->effective_value == NULL)
+               add_it = 1;
+       else if ((flags & CFZY_FILTER_F_USR_CHANGED) &&
+                strcmp2(n->user_value, n->old_user_value))
+               add_it = 1;
+       else if ((flags & CFZY_FILTER_F_EFF_CHANGED) &&
+                strcmp2(n->effective_value, n->old_effective_value))
+               add_it = 1;
+
+       if (add_it == 1) {
+               if (cfzy_list_add_tail(node_list, n) < 0) {
+                       LOG(ERR, "cannot add node in list\n");
+                       return -1;
+               }
+       }
+
+       /* children nodes */
+       TAILQ_FOREACH(c, &n->children, child_next) {
+               if (__filter(c, node_list, flags) < 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+/* Get a list of nodes matching conditions (for instance the nodes
+ * that have no user value) */
+struct cfzy_list_head *
+cfzy_conftree_filter(struct cfzy_conftree *conftree, int flags)
+{
+       struct cfzy_list_head *node_list = NULL;
+
+       node_list = cfzy_list_alloc();
+       if (node_list == NULL) {
+               LOG(ERR, "cannot allocate node list\n");
+               return NULL;
+       }
+
+       if (__filter(conftree->root, node_list, flags) < 0) {
+               LOG(ERR, "cannot filter nodes\n");
+               cfzy_list_free(node_list, NULL);
+               return NULL;
+       }
+
+       return node_list;
+}
+
+static int __save_values(struct cfzy_confnode *n)
+{
+       struct cfzy_confnode *c;
+
+       /* duplicate user value */
+       if (n->old_user_value != NULL) {
+               free(n->old_user_value);
+               n->old_user_value = NULL;
+       }
+       if (n->user_value != NULL) {
+               n->old_user_value = strdup(n->user_value);
+               if (n->old_user_value == NULL) {
+                       LOG(ERR, "cannot duplicate value\n");
+                       return -1;
+               }
+       }
+
+       /* duplicate effective value */
+       if (n->old_effective_value != NULL) {
+               free(n->old_effective_value);
+               n->old_effective_value = NULL;
+       }
+       if (n->effective_value != NULL) {
+               n->old_effective_value = strdup(n->effective_value);
+               if (n->old_effective_value == NULL) {
+                       LOG(ERR, "cannot duplicate value\n");
+                       return -1;
+               }
+       }
+
+       /* do the same on children */
+       TAILQ_FOREACH(c, &n->children, child_next) {
+               if (__save_values(c) < 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+int cfzy_conftree_save_values(struct cfzy_conftree *conftree)
+{
+       return __save_values(conftree->root);
+}
diff --git a/tools/cfzy/libconfizery/cfzy_conftree.h b/tools/cfzy/libconfizery/cfzy_conftree.h
new file mode 100644 (file)
index 0000000..ea77c1e
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * Confizery configuration tree
+ *
+ * This module defines a structure cfzy_conftree that stores the full
+ * configuration tree (composed of cfzy_confnode structures), and some
+ * meta data like the number of nodes in the tree, a hash table used
+ * to do a node lookup by its name, the path of the configuration tree
+ * file, ...
+ */
+
+#ifndef _CFZY_CONFTREE_H_
+#define _CFZY_CONFTREE_H_
+
+#include "cfzy_confnode.h"
+
+struct cfzy_htable;
+
+/**
+ * Structure storing the config tree
+ */
+struct cfzy_conftree {
+       char *path;                       /**< path of conftree file */
+       struct cfzy_confnode *root;       /**< pointer to root node */
+       int count;                        /**< number of nodes */
+       struct cfzy_htable *nodes_htable; /**< htable of nodes, indexed by name */
+       struct cfzy_confnode_list prio_list; /**< ordered list of nodes */
+};
+
+/**
+ * Create a new configuration tree
+ *
+ * Parse a configuration tree and allocate a new conftree
+ * structure. The structure must be freed with cfzy_conftree_free().
+ *
+ * @param filename
+ *   name of the configuration tree file
+ * @return
+ *   the allocated conftree structure, or NULL on error
+ */
+struct cfzy_conftree *cfzy_conftree_new(const char *filename);
+
+/**
+ * Free a configuration tree structure
+ *
+ * Free a configuration tree structure previously allocated with
+ * cfzy_conftree_new().
+ *
+ * @param conftree
+ *   the configuration tree structure to be freed
+ */
+void cfzy_conftree_free(struct cfzy_conftree *conftree);
+
+/**
+ * Dump a configuration tree structure in a file
+ *
+ * @param conftree
+ *   the configuration tree structure to be freed
+ * @param filename
+ *   file where conf should be dumped
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_conftree_dump(const struct cfzy_conftree *conftree,
+                      const char *filename);
+
+/**
+ * Evaluate a configuration tree structure
+ *
+ * Parse all nodes following priority list and set the effective_value
+ * field by calling cfzy_confnode_evaluate() which takes care of
+ * default values, user values, and dependencies.
+ *
+ * @param conftree
+ *   the configuration tree structure
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_conftree_evaluate(struct cfzy_conftree *conftree);
+
+/**
+ * Save values of configuration tree
+ *
+ * For each node, copy the effective_value field in
+ * old_effective_value and the user_value field in old_user_value.
+ * These values can be used later with cfzy_conftree_filter() to
+ * compare the old values with the new ones.
+ *
+ * @param conftree
+ *   the configuration tree structure
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_conftree_save_values(struct cfzy_conftree *conftree);
+
+/* flags to be used with cfzy_conftree_filter() */
+#define CFZY_FILTER_F_USR_UNSET     0x01
+#define CFZY_FILTER_F_EFF_UNSET     0x02
+#define CFZY_FILTER_F_USR_CHANGED   0x04
+#define CFZY_FILTER_F_EFF_CHANGED   0x08
+
+/**
+ * Get a list of nodes matching conditions
+ *
+ * Return the list of nodes in configuration tree that match
+ * conditions. Example: cfzy_conftree_filter(contree,
+ * CFZY_FILTER_F_USR_UNSET) will return all nodes that have no user
+ * value (set to NULL).
+ *
+ * @param conftree
+ *   the configuration tree structure
+ * @param flags
+ *   list of conditions (CFZY_FILTER_F_*)
+ * @return
+ *   0 on success, negative on error
+ */
+struct cfzy_list_head *
+cfzy_conftree_filter(struct cfzy_conftree *conftree, int flags);
+
+#endif /* _CFZY_CONFTREE_H_ */
diff --git a/tools/cfzy/libconfizery/cfzy_conftree_parser.c b/tools/cfzy/libconfizery/cfzy_conftree_parser.c
new file mode 100644 (file)
index 0000000..a282e83
--- /dev/null
@@ -0,0 +1,790 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <libgen.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include "cfzy_log.h"
+#include "cfzy_string.h"
+#include "cfzy_expr.h"
+#include "cfzy_htable.h"
+#include "cfzy_conftree.h"
+#include "cfzy_confnode.h"
+#include "cfzy_conftree_parser.h"
+
+#define LOG(level, fmt, args...)                               \
+       CFZY_LOG("conftree_parser", level, fmt, ##args)
+
+/*
+ * Structure that stores the state of the configuration tree parser.
+ */
+struct conftree_parse_status {
+       const char *filename;           /* name of file beeing parsed */
+       FILE *f;                        /* pointer to file beeing parsed */
+       int linenum;                    /* current line number in file */
+       char *curline;                  /* pointer to current line, or
+                                          NULL if no current line */
+       struct cfzy_token_list *tklist; /* pointer to current token
+                                          list of NULL if no current line */
+       struct cfzy_confnode *parent;   /* current parent node */
+};
+
+static int __cfzy_conftree_parse(const char *filename,
+                                struct cfzy_confnode **pparent);
+
+/* return true if line is empty or contains only spaces/comments */
+static int line_is_empty(const char *buf)
+{
+       while (*buf != '\0' && *buf != '#') {
+               if (!isspace(*buf))
+                       return 0;
+               buf++;
+       }
+       return 1;
+}
+
+/*
+ * Add a token in the tklist structure given as argument.
+ *
+ * 'buf': pointer to the beginning of line buffer.
+ * 'offset': offset in the line where start parsing
+ *
+ * Return the number of consumed bytes if a token is found
+ * Return 0 if there is no more token
+ * Return -1 on error
+ */
+static int
+append_token(struct cfzy_token_list *tklist, const char *buf, int offset)
+{
+       struct cfzy_token *tok;
+       const char *s;
+       unsigned len, space_len = 0;
+       char *retbuf;
+
+       /* skip spaces */
+       s = buf + offset;
+       while (*s != '\0' && isspace(*s))
+               s++;
+       if (*s == '\0' || *s == '#')
+               return 0;
+
+       tok = malloc(sizeof(struct cfzy_token));
+       if (tok == NULL) {
+               LOG(ERR, "not enough memory\n");
+               return -1;
+       }
+
+       space_len = s - buf - offset;
+       tok->offset = s - buf;
+
+       /* if it's a quote, unquote the string */
+       if (*s == '"' || *s == '\'') {
+               retbuf = cfzy_string_unquote(s, &len);
+               if (retbuf == NULL)
+                       goto free;
+       }
+       /* else wait a space */
+       else {
+               retbuf = strdup(s);
+               if (retbuf == NULL) {
+                       LOG(ERR, "not enough memory\n");
+                       goto free;
+               }
+
+               for (len = 0;
+                    s[len] != '\0' && !isspace(s[len]) && s[len] != '#';
+                    len ++)
+                       ;
+
+               retbuf[len] = '\0';
+       }
+
+       /* fill token structure and append it in tklist */
+       tok->str = retbuf;
+       TAILQ_INSERT_TAIL(&tklist->list, tok, next);
+       tklist->n_token ++;
+
+       return len + space_len;
+
+ free:
+       free(tok);
+       return -1;
+}
+
+/* free a token list previously allocated with tokenize() */
+static void free_token_list(struct cfzy_token_list *tklist)
+{
+       struct cfzy_token *tok;
+
+       while ((tok = TAILQ_FIRST(&tklist->list)) != NULL) {
+               TAILQ_REMOVE(&tklist->list, tok, next);
+               free(tok->str);
+               free(tok);
+       }
+       free(tklist);
+}
+
+/*
+ * Parse the line contained in buffer 'linebuf'. This function fills a
+ * tklist structure, composed of several tokens.
+ * Return the tklist pointer on success or NULL on error.
+ */
+static struct cfzy_token_list *tokenize(const char *linebuf)
+{
+       struct cfzy_token_list *tklist;
+       int ret, offset = 0;
+
+       tklist = malloc(sizeof(struct cfzy_token_list));
+       if (tklist == NULL) {
+               LOG(ERR, "not enough memory\n");
+               return NULL;
+       }
+
+       memset(tklist, 0, sizeof(struct cfzy_token_list));
+       TAILQ_INIT(&tklist->list);
+
+       /* read tokens from buf and append it to tklist structure */
+       do {
+               ret = append_token(tklist, linebuf, offset);
+               if (ret < 0) {
+                       free_token_list(tklist);
+                       return NULL;
+               }
+               offset += ret;
+       } while (ret != 0);
+
+       tklist->linebuf = linebuf;
+       return tklist;
+}
+
+/* duplicate buf, replacing variables like $(VAR) by their values */
+char *replace_variables(const char *buf)
+{
+       char *s, *buf_dup, *var;
+       const char *start;
+       char *out = NULL;
+       char c;
+
+       buf_dup = strdup(buf);
+       if (buf_dup == NULL) {
+               LOG(ERR, "not enough memory\n");
+               return NULL;
+       }
+
+       s = buf_dup;
+
+       for ( ; s[0] != '\0' ; s++) {
+
+               start = s;
+
+               /* wait a "$(" */
+               for ( ; s[0] != '\0' && (s[0] != '$' || s[1] != '('); s++);
+               c = s[0];
+               s[0] = '\0';
+               if (cfzy_string_asprintf(&out, "%s", start) < 0)
+                       goto fail;
+
+               if (c == '\0')
+                       break;
+
+               s += 2;
+               start = s;
+
+               for ( ; s[0] != '\0' && s[0] != ')'; s++);
+               c = s[0];
+               if (c == '\0')
+                       goto fail;
+
+               s[0] = '\0';
+               var = getenv(start);
+
+               if (var != NULL) {
+                       if (cfzy_string_asprintf(&out, var) < 0)
+                               goto fail;
+               }
+
+       }
+
+       free(buf_dup);
+       return out;
+
+ fail:
+       free(buf_dup);
+       if (out != NULL)
+               free(out);
+       return NULL;
+}
+
+/*
+ * Fill state structure with current line. If state->curline is
+ * already set, the function does nothing, else it will read a new
+ * line in the file and tokenize it. Return 0 on success, and -1 on
+ * error. If state->curline is still NULL after a call to this
+ * function, we reached the end of the file.
+ */
+static int get_curline(struct conftree_parse_status *state)
+{
+       char buf[BUFSIZ];
+       int line_len;
+       char *line;
+
+       /* nothing to do */
+       if (state->curline != NULL)
+               return 0;
+
+       state->curline = fgets(buf, sizeof(buf), state->f);
+
+       /* end of file */
+       if (state->curline == NULL) {
+               LOG(DEBUG, "EOF\n");
+               return 0;
+       }
+
+       state->linenum ++;
+       line_len = strlen(state->curline);
+
+       /* if line ends with '\', get another line */
+       while (line_len >= 2 &&
+              state->curline[line_len - 1] == '\n' &&
+              state->curline[line_len - 2] == '\\') {
+               if (line_len >= (int)sizeof(buf) - 1)
+                       break;
+
+               line = fgets(buf + line_len - 2,
+                            sizeof(buf) - line_len - 2, state->f);
+               if (line == NULL) /* EOF */
+                       break;
+
+               line_len = strlen(state->curline);
+               state->linenum ++;
+       }
+
+       state->curline = replace_variables(buf);
+       if (state->curline == NULL) {
+               LOG(ERR, "Cannot eval variables\n");
+               return -1;
+       }
+
+       state->tklist = tokenize(state->curline);
+       if (state->tklist == NULL) {
+               LOG(ERR, "Cannot parse line\n");
+               free(state->curline);
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * This function is called when we processed the current line. It will
+ * free allocated memory (token list) and set state->curline to NULL.
+ */
+static void eat_curline(struct conftree_parse_status *state)
+{
+       if (state->curline != NULL) {
+               free(state->curline);
+               state->curline = NULL;
+       }
+       if (state->tklist != NULL) {
+               free_token_list(state->tklist);
+               state->tklist = NULL;
+       }
+}
+
+/* Try to parse node help. Return NO_MATCH if the current line does
+ * not match the 'help' token, SUCCESS if help is successfully parsed,
+ * or ERROR on error. */
+static enum cfzy_parse_return
+parse_help(struct cfzy_confnode *n, struct conftree_parse_status *state)
+{
+       struct cfzy_token_list *tklist;
+       struct cfzy_token *tok;
+       char *buf;
+
+       if (get_curline(state) < 0)
+               return ERROR;
+
+       buf = state->curline;
+
+       /* EOF */
+       if (buf == NULL)
+               return NO_MATCH;
+
+       /* we want 1 token */
+       tklist = state->tklist;
+       if (tklist->n_token != 1)
+               return NO_MATCH;
+
+       /* we want a "help" token */
+       tok = TAILQ_FIRST(&tklist->list);
+       if (strcmp(tok->str, "help") && strcmp(tok->str, "---help---"))
+               return NO_MATCH;
+
+       LOG(INFO, "parse help\n");
+       eat_curline(state);
+
+       /* parse help content */
+       while (1) {
+
+               /* read next line */
+               if (get_curline(state) < 0)
+                       return ERROR;
+
+               buf = state->curline;
+
+               /* EOF */
+               if (buf == NULL)
+                       return SUCCESS;
+
+               /* skip empty lines */
+               if (line_is_empty(buf)) {
+                       eat_curline(state);
+                       continue;
+               }
+
+               /* if line does not start with space, this is this end
+                  of help parsing */
+               if (!isspace(*buf))
+                       return SUCCESS;
+
+               /* append string in buffer, stripping starting spaces */
+               tok = TAILQ_FIRST(&state->tklist->list);
+               if (cfzy_string_asprintf(&n->help, "%s",
+                                 buf + tok->offset) < 0)
+                       return ERROR;
+
+               eat_curline(state);
+       }
+}
+
+/* Try to parse the "source" command. Return NO_MATCH if the current
+ * line does not match the "source" token, SUCCESS if the file is
+ * successfully sourced, or ERROR on error. */
+static enum cfzy_parse_return source_file(struct conftree_parse_status *state)
+{
+       struct cfzy_token_list *tklist;
+       struct cfzy_token *tok;
+
+       if (get_curline(state) < 0)
+               return ERROR;
+
+       /* EOF */
+       if (state->curline == NULL)
+               return NO_MATCH;
+
+       tklist = state->tklist;
+       tok = TAILQ_FIRST(&tklist->list);
+
+       /* syntax is: source FILE_NAME */
+       if (tklist->n_token != 2 || strcmp(tok->str, "source"))
+               return NO_MATCH;
+
+       tok = TAILQ_NEXT(tok, next);
+       LOG(INFO, "source <%s>\n", tok->str);
+
+       /* parse new file */
+       if (__cfzy_conftree_parse(tok->str, &state->parent) < 0) {
+               LOG(ERR, "error in sourced file\n");
+               return ERROR;
+       }
+
+       eat_curline(state);
+       return SUCCESS;
+}
+
+/* Try to create a new node command ("config", "menuconfig", "choice",
+ * "if", ...). Return NO_MATCH if the current line is not a new node
+ * command, SUCCESS if a new node is created, or ERROR on error. This
+ * function does not parse the attributes of the node but only the
+ * first line.  */
+static enum cfzy_parse_return
+new_node(struct conftree_parse_status *state, struct cfzy_confnode **nodep)
+{
+       struct cfzy_token_list *tklist;
+       enum cfzy_parse_return ret;
+
+       if (get_curline(state) < 0)
+               return ERROR;
+
+       /* EOF */
+       if (state->curline == NULL)
+               return NO_MATCH;
+
+       tklist = state->tklist;
+
+       *nodep = cfzy_confnode_alloc(state->parent->conftree);
+       if (*nodep == NULL) {
+               LOG(ERR, "cannot allocate node\n");
+               return ERROR;
+       }
+       (*nodep)->parent = state->parent;
+
+       ret = cfzy_confnode_choice_new(*nodep, tklist);
+       if (ret != NO_MATCH)
+               return ret;
+       ret = cfzy_confnode_choiceconfig_new(*nodep, tklist);
+       if (ret != NO_MATCH)
+               return ret;
+       ret = cfzy_confnode_comment_new(*nodep, tklist);
+       if (ret != NO_MATCH)
+               return ret;
+       ret = cfzy_confnode_config_new(*nodep, tklist);
+       if (ret != NO_MATCH)
+               return ret;
+       ret = cfzy_confnode_if_new(*nodep, tklist);
+       if (ret != NO_MATCH)
+               return ret;
+       ret = cfzy_confnode_intconfig_new(*nodep, tklist);
+       if (ret != NO_MATCH)
+               return ret;
+       ret = cfzy_confnode_menu_new(*nodep, tklist);
+       if (ret != NO_MATCH)
+               return ret;
+       ret = cfzy_confnode_menuconfig_new(*nodep, tklist);
+       if (ret != NO_MATCH)
+               return ret;
+       ret = cfzy_confnode_strconfig_new(*nodep, tklist);
+       if (ret != NO_MATCH)
+               return ret;
+
+       cfzy_confnode_free(*nodep);
+       return NO_MATCH;
+}
+
+/* Try to create a new node with all its attributes and help. Return
+ * NO_MATCH if the current line is not a new node command, SUCCESS if
+ * a new node is created, or ERROR on error. */
+static enum cfzy_parse_return
+parse_confnode(struct conftree_parse_status *state)
+{
+       char *buf;
+       struct cfzy_confnode *n;
+       enum cfzy_parse_return ret;
+       struct cfzy_token_list *tklist;
+
+       if (get_curline(state) < 0)
+               return ERROR;
+
+       buf = state->curline;
+
+       /* EOF */
+       if (buf == NULL)
+               return NO_MATCH;
+
+       /* line must not start with a space */
+       if (isspace(*buf))
+               return NO_MATCH;
+
+       /* if it's a new node command */
+       ret = new_node(state, &n);
+       if (ret == ERROR || ret == NO_MATCH)
+               return ret;
+
+       /* save parsing infos */
+       n->filename = strdup(state->filename);
+       if (n->filename == NULL) {
+               cfzy_confnode_free(n);
+               return ERROR;
+       }
+       n->linenum = state->linenum;
+
+       TAILQ_INSERT_TAIL(&state->parent->children, n, child_next);
+       eat_curline(state);
+
+       /* parse node attributes */
+       while (1) {
+
+               /* fill state structure with current or next line */
+               if (get_curline(state) < 0)
+                       return ERROR;
+
+               buf = state->curline;
+               tklist = state->tklist;
+
+               /* EOF */
+               if (buf == NULL)
+                       return SUCCESS;
+
+               /* skip empty lines */
+               if (line_is_empty(buf)) {
+                       eat_curline(state);
+                       continue;
+               }
+
+               /* if line does not start with space, this node is
+                * parsed, break */
+               if (!isspace(*buf))
+                       break;
+
+               /* check if it's a node attribute */
+               ret = cfzy_confnode_add_attr(n, tklist);
+               if (ret == ERROR)
+                       return ERROR;
+               if (ret == SUCCESS) {
+                       eat_curline(state);
+                       continue;
+               }
+
+               /* check if it's a help indication */
+               ret = parse_help(n, state);
+               if (ret == ERROR)
+                       return ERROR;
+               if (ret == SUCCESS)
+                       break;
+
+               /* the attribute is invalid */
+               LOG(ERR, "invalid node attribute\n");
+               return ERROR;
+       }
+
+       /* enter in a new node */
+       if (n->flags & CFZY_F_IS_DIR)
+               state->parent = n;
+
+       return SUCCESS;
+}
+
+/* Try to match the command that closes the current parent node, if
+ * it's a directory. Return NO_MATCH if the line does not match this
+ * command, SUCCESS if the parent node is closed (in this case,
+ * state->parent is updated), or ERROR on error. */
+static enum cfzy_parse_return
+close_dir(struct conftree_parse_status *state)
+{
+       struct cfzy_token_list *tklist;
+       struct cfzy_token *tok;
+       struct cfzy_confnode *n;
+
+       n = state->parent;
+       if ((n->flags & CFZY_F_IS_DIR) == 0)
+               return NO_MATCH;
+
+       /* fill state structure with current or next line */
+       if (get_curline(state) < 0)
+               return ERROR;
+
+       /* EOF */
+       if (state->curline == NULL)
+               return NO_MATCH;
+
+       tklist = state->tklist;
+       if (tklist->n_token != 1)
+               return NO_MATCH;
+
+       /* token must be "endchoice", "endmenu", "endif", ... */
+       tok = TAILQ_FIRST(&tklist->list);
+       if (strncmp(tok->str, "end", 3))
+               return NO_MATCH;
+       if (strcmp(tok->str + 3, cfzy_confnode_get_type_str(n)))
+               return NO_MATCH;
+       if (cfzy_confnode_close(n) < 0) {
+               LOG(ERR, "cannot close node\n");
+               return ERROR;
+       }
+
+       state->parent = n->parent;
+       return SUCCESS;
+}
+
+/*
+ * Parse the configuration tree by reading the file stored in the
+ * 'state' structure. Return SUCCESS or ERROR.
+ */
+static enum cfzy_parse_return parse_conftree(struct conftree_parse_status *state)
+{
+       enum cfzy_parse_return ret;
+       char *buf;
+
+       /* browse all lines of the file */
+       while (1) {
+
+               /* fill state structure with current or next line */
+               if (get_curline(state) < 0)
+                       return ERROR;
+
+               buf = state->curline;
+
+               /* EOF */
+               if (buf == NULL)
+                       break;
+
+               LOG(DEBUG, "parent=%s: %s",
+                   cfzy_confnode_name(state->parent), buf);
+
+               /* skip empty / comments lines */
+               if (line_is_empty(buf)) {
+                       eat_curline(state);
+                       continue;
+               }
+
+               /* if the line does not start with a space, syntax error */
+               if (isspace(*buf)) {
+                       LOG(ERR, "line starts with space\n");
+                       return ERROR;
+               }
+
+               /* if it's a source command, parse the new file */
+               ret = source_file(state);
+               if (ret == ERROR)
+                       return ERROR;
+               if (ret == SUCCESS)
+                       continue;
+
+               /* if the command closes a menu, choice, ... */
+               ret = close_dir(state);
+               if (ret == ERROR)
+                       return ERROR;
+               if (ret == SUCCESS) {
+                       eat_curline(state);
+                       continue;
+               }
+
+               /* parse new node */
+               ret = parse_confnode(state);
+               if (ret == ERROR)
+                       return ERROR;
+               if (ret == SUCCESS)
+                       continue;
+
+               /* invalid command */
+               LOG(ERR, "invalid command\n");
+               return ERROR;
+       }
+
+       return SUCCESS;
+}
+
+/*
+ * Parse the configuration tree by reading "filename", knowing that
+ * the current parent node is pointed by pparent (which can be
+ * modified at the end of the function). This function will fill open
+ * the file, and fill a state structure for the parser, then call
+ * parse_conftree(). Return 0 on success and -1 on error.
+ */
+static int
+__cfzy_conftree_parse(const char *filename, struct cfzy_confnode **pparent)
+{
+       struct conftree_parse_status state;
+       FILE *f = NULL;
+       char old_cwd_buf[PATH_MAX];
+       char *cwd_buf = NULL, *cwd_ptr = NULL, *old_cwd = NULL;
+       int ret;
+
+       memset(&state, 0, sizeof(state));
+
+       old_cwd = getcwd(old_cwd_buf, sizeof(old_cwd_buf));
+       if (old_cwd == NULL) {
+               LOG(ERR, "getcwd failed: %s\n", strerror(errno));
+               goto fail;
+       }
+
+       f = fopen(filename, "r");
+       if (f == NULL) {
+               LOG(ERR, "cannot find file <%s>\n", filename);
+               goto fail;
+       }
+
+       cwd_buf = strdup(filename);
+       if (cwd_buf == NULL) {
+               LOG(ERR, "not enough memory\n");
+               goto fail;
+       }
+       cwd_ptr = dirname(cwd_buf);
+       ret = chdir(cwd_ptr);
+       if (ret < 0) {
+               LOG(ERR, "chdir failed: %s\n", strerror(errno));
+               goto fail;
+       }
+
+       state.filename = filename;
+       state.f = f;
+       state.linenum = 0;
+       state.parent = *pparent;
+       state.curline = NULL;
+
+       /* browse all lines of the file */
+       ret = parse_conftree(&state);
+       if (ret != SUCCESS) {
+               /* node will be freed by caller (cfzy_conftree_new) */
+               LOG(ERR, "parse error at %s:%d\n", filename, state.linenum);
+               LOG(ERR, ">>> %s", state.curline);
+               goto fail;
+       }
+
+       eat_curline(&state); /* will free token_list */
+       fclose(f);
+       free(cwd_buf);
+       if (chdir(old_cwd) < 0) {
+               LOG(ERR, "chdir back to <%s> failed: %s\n",
+                   old_cwd, strerror(errno));
+               return -1;
+       }
+
+       *pparent = state.parent;
+       return 0;
+
+ fail:
+       if (state.f != NULL)
+               eat_curline(&state); /* will free token_list */
+       if (f != NULL)
+               fclose(f);
+       if (cwd_buf != NULL)
+               free(cwd_buf);
+       if (old_cwd != NULL)
+               if (chdir(old_cwd) < 0)
+                       LOG(ERR, "chdir back to <%s> failed: %s\n",
+                           old_cwd, strerror(errno));
+
+       return -1;
+}
+
+/* External api for parsing a new configuration tree file. Return 0 on
+ * success and -1 on error. */
+int cfzy_conftree_parse(const char *filename, struct cfzy_conftree *conftree)
+{
+       int ret;
+       struct cfzy_confnode *parent = conftree->root;
+
+       ret = __cfzy_conftree_parse(filename, &parent);
+       if (ret < 0)
+               return ret;
+
+       if (parent != conftree->root) {
+               LOG(ERR, "Node <%s> not closed properly\n",
+                   cfzy_confnode_name(parent));
+               return -1;
+       }
+
+       return 0;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_conftree_parser.h b/tools/cfzy/libconfizery/cfzy_conftree_parser.h
new file mode 100644 (file)
index 0000000..8e17fbf
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * Confizery configuration tree parser
+ *
+ * This modules provides an API to parse the configuration tree file
+ * (equivalent to Kconfig file in linux terminology) which describes
+ * the structure of the tree, the option list, the dependencies, the
+ * default values...
+ */
+
+
+#ifndef _CFZY_CONFTREE_PARSER_H_
+#define _CFZY_CONFTREE_PARSER_H_
+
+#include <sys/queue.h>
+
+struct cfzy_conftree;
+
+/**
+ * Return value for some parsing functions.
+ */
+enum cfzy_parse_return {
+       SUCCESS,
+       NO_MATCH,
+       ERROR,
+};
+
+/**
+ * This is the structure defining a token. It is used by the
+ * configuration tree parser. A token is either a word or a string
+ * surrounded by double quotes.
+ */
+struct cfzy_token {
+       TAILQ_ENTRY(cfzy_token) next; /**< pointer to next token */
+       char *str;               /**< string */
+       unsigned offset;         /**< offset in line */
+};
+
+/**
+ * A list of token, and a pointer to the initial line buffer
+ */
+struct cfzy_token_list {
+       TAILQ_HEAD(, cfzy_token) list; /**< list of tokens */
+       unsigned n_token;        /**< number of tokens */
+       const char *linebuf;     /**< the line buffer */
+};
+
+/**
+ * Parse configuration tree file and fill the conftree
+ *
+ * This function is called by cfzy_conftree_new() to fill the newly
+ * allocated tree.
+ *
+ * @param filename
+ *   path to configuration tree file
+ * @param conftree
+ *   an empty configuration tree
+ * @return
+ *   0 on success, or negative on error
+ */
+int cfzy_conftree_parse(const char *filename, struct cfzy_conftree *conftree);
+
+#endif /* _CFZY_CONFTREE_PARSER_H_ */
diff --git a/tools/cfzy/libconfizery/cfzy_dotconfig.c b/tools/cfzy/libconfizery/cfzy_dotconfig.c
new file mode 100644 (file)
index 0000000..4e86934
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "cfzy_log.h"
+#include "cfzy_htable.h"
+#include "cfzy_string.h"
+#include "cfzy_file.h"
+#include "cfzy_conftree.h"
+#include "cfzy_confnode.h"
+
+#define LOG(level, fmt, args...)                               \
+       CFZY_LOG("dotconfig", level, fmt, ##args)
+
+/* Allocate and return string containing the value of the
+ * option. Buffer must point to the option (after the '=' in the
+ * .config), and it can be quoted. The string must be freed by the
+ * user. */
+static char *dotconfig_get_optval(const char *buf)
+{
+       const char *s;
+       unsigned i, len;
+       char *retbuf;
+
+       /* skip spaces */
+       s = buf;
+       while (*s != '\0' && isspace(*s))
+               s++;
+       if (*s == '\0' || *s == '#')
+               return 0;
+
+       /* if it's a quote, unquote the string */
+       if (*s == '"' || *s == '\'') {
+               retbuf = cfzy_string_unquote(s, &len);
+       }
+       else {
+               retbuf = strdup(s);
+               if (retbuf == NULL)
+                       return NULL;
+
+               for (i = 0; s[i] != '\0' && s[i] != '\n' && s[i] != '#'; i ++)
+                       ;
+
+               retbuf[i] = '\0';
+       }
+       return retbuf;
+}
+
+/* parse a line of .config: fill the uservalue of nodes in the
+ * conftree structure */
+static int dotconfig_parse_line(struct cfzy_conftree *conftree, const char *line)
+{
+       struct cfzy_confnode *n;
+       char buf[BUFSIZ];
+       char *nodename, *s;
+       char *optval;
+
+       /* copy line in a writable buffer */
+       buf[sizeof(buf) - 1] = '\0';
+       strncpy(buf, line, sizeof(buf) - 1);
+       s = buf;
+
+       /* skip spaces */
+       while (*s != '\0' && isspace(*s))
+               s++;
+
+       /* nothing to do, empty line */
+       if (*s == '\0')
+               return 0;
+
+       /* skip the first "#", expecting "is not set" */
+       if (*s == '#') {
+               s++;
+               while (*s != '\0' && isspace(*s))
+                       s++;
+
+               /* nothing to do, it's a comment */
+               if (*s == '\0')
+                       return 0;
+
+               /* get node name, ignore invalid names */
+               if (strncmp("CONFIG_", s, strlen("CONFIG_")))
+                       return 0;
+               nodename = s + strlen("CONFIG_");
+               s = nodename;
+
+               /* skip spaces */
+               while (*s != '\0' && !isspace(*s))
+                       s++;
+
+               /* nothing to do, it's a comment */
+               if (*s == '\0')
+                       return 0;
+
+               *s = '\0';
+               s ++;
+
+               /* should end with "is not set", else it's a comment */
+               if (strcmp(s, "is not set\n"))
+                       return 0;
+
+               optval = strdup("n");
+               if (optval == NULL)
+                       return -1;
+       }
+       else {
+               /* get node name, ignore invalid names */
+               if (strncmp("CONFIG_", s, strlen("CONFIG_")))
+                       return -1;
+               nodename = s + strlen("CONFIG_");
+
+               while (*s != '\0' && *s != '=')
+                       s++;
+               /* invalid line */
+               if (*s != '=')
+                       return -1;
+
+               *s = '\0';
+               optval = dotconfig_get_optval(s + 1);
+               if (optval == NULL)
+                       return -1;
+       }
+
+       /* find the node in the conf htable */
+       n = cfzy_htable_lookup(conftree->nodes_htable, nodename);
+       if (n == NULL) {
+               LOG(NOTICE, "ignore unknown symbol <%s>\n", nodename);
+               free(optval);
+               return 0;
+       }
+
+       /* check that value is valid */
+       if (cfzy_confnode_str2bool(n, optval) < 0) {
+               LOG(ERR, "invalid option value %s=<%s>\n", nodename, optval);
+               free(optval);
+               return -1;
+       }
+
+       /* set value in node */
+       LOG(DEBUG, "%s=<%s>\n", nodename, optval);
+       if (cfzy_confnode_set_uservalue(n, optval) < 0) {
+               LOG(ERR, "cannot set value %s=<%s>\n", nodename, optval);
+               free(optval);
+               return -1;
+       }
+
+       free(optval);
+       return 0;
+}
+
+/* parse .config */
+int cfzy_dotconfig_read(struct cfzy_conftree *conftree, const char *filename)
+{
+       FILE *f;
+       char buf[BUFSIZ];
+       int err = 0;
+
+       LOG(INFO, "dotconfig read <%s>\n", filename);
+
+       f = fopen(filename, "r");
+       if (f == NULL) {
+               LOG(ERR, "cannot find file <%s>\n", filename);
+               return -1;
+       }
+       while (fgets(buf, sizeof(buf), f) != NULL) {
+
+               if (dotconfig_parse_line(conftree, buf) < 0) {
+                       err = -1;
+                       break;
+               }
+       }
+       fclose(f);
+
+       return err;
+}
+
+/* write .config */
+int cfzy_dotconfig_write(const struct cfzy_conftree *conftree,
+                        const char *filename)
+{
+       FILE *f, *tmp_f;
+       const struct cfzy_confnode *c;
+       int ret = 0;
+
+       LOG(INFO, "open old dotconfig <%s>\n", filename);
+
+       /* open old file in read mode */
+
+       f = fopen(filename, "r");
+       if (f == NULL && errno != ENOENT) {
+               printf("cannot open file <%s>: %s\n",
+                      filename, strerror(errno));
+               return -1;
+       }
+
+       /* old file exists */
+       if (f != NULL) {
+
+               /* write C header in temp file */
+               tmp_f = tmpfile();
+               if (tmp_f == NULL) {
+                       printf("cannot open temp file\n");
+                       return -1;
+               }
+
+               /* dump all the children of root node */
+               TAILQ_FOREACH(c, &conftree->root->children, child_next) {
+                       ret = cfzy_confnode_dotconfig_write(c, tmp_f);
+                       if (ret < 0)
+                               break;
+               }
+
+               /* files are the same, nothing to do */
+               if (cfzy_file_compare(f, tmp_f) == 0) {
+                       LOG(INFO, "already up to date: <%s>\n", filename);
+                       fclose(tmp_f);
+                       fclose(f);
+                       return 0;
+               }
+
+               fclose(tmp_f);
+               fclose(f);
+       }
+
+       /* reopen the file in write mode */
+
+       LOG(INFO, "write new C header <%s>\n", filename);
+
+       f = fopen(filename, "w");
+       if (f == NULL) {
+               LOG(ERR, "cannot open file <%s>\n", filename);
+               return -1;
+       }
+
+       /* dump all the children of root node */
+       TAILQ_FOREACH(c, &conftree->root->children, child_next) {
+               ret = cfzy_confnode_dotconfig_write(c, f);
+               if (ret < 0)
+                       break;
+       }
+
+       fclose(f);
+
+       return ret;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_dotconfig.h b/tools/cfzy/libconfizery/cfzy_dotconfig.h
new file mode 100644 (file)
index 0000000..53dd8dd
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * Confizery dotconfig parsing/generation
+ *
+ * This modules provides an API to parse or generate a .config file
+ * from a configuration tree.
+ */
+
+#ifndef _CFZY_DOTCONFIG_H_
+#define _CFZY_DOTCONFIG_H_
+
+/**
+ * Write configuration in a .config file
+ *
+ * Write the configuration values (.config like) in a file. This file
+ * is designed to be sourced in a shell script or in a makefile.
+ *
+ * If the file already exists and has the same content, nothing is
+ * written in it.
+ *
+ * @param conftree
+ *   the configuration tree
+ * @param filename
+ *   name of the output file
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_dotconfig_write(const struct cfzy_conftree *conftree,
+                        const char *filename);
+
+/**
+ * Read and parse a dotconfig configuration file
+ *
+ * Read the configuration values (.config like) from a file, and set
+ * the values in the configuration tree.
+ *
+ * @param conftree
+ *   the configuration tree
+ * @param filename
+ *   name of the output file
+ * @return
+ *   0 on success, negative on error
+ */
+int cfzy_dotconfig_read(struct cfzy_conftree *conftree, const char *filename);
+
+#endif
diff --git a/tools/cfzy/libconfizery/cfzy_expr.c b/tools/cfzy/libconfizery/cfzy_expr.c
new file mode 100644 (file)
index 0000000..d911e22
--- /dev/null
@@ -0,0 +1,571 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/queue.h>
+
+#include "cfzy_list.h"
+#include "cfzy_expr.h"
+
+/* #define DEBUG */
+
+#ifdef DEBUG
+#define debug_printf(args...) fprintf(stderr, args)
+#else
+#define debug_printf(args...) do { } while(0)
+#endif
+
+/*
+ * Return the operator (or the variable name) of the root node of the
+ * given expression.
+ */
+static const char *op_print(const struct cfzy_expr *exp)
+{
+       switch (exp->optype) {
+       case OP_OR:
+               return "||";
+       case OP_AND:
+               return "&&";
+       case OP_EQUAL:
+               return "==";
+       case OP_OBRACKET:
+               return "(";
+       case OP_NOT:
+               return "!";
+       case OP_CBRACKET:
+               return ")";
+       case OP_VAR:
+               return exp->varname;
+       default:
+               return NULL;
+       }
+}
+
+/* recursive function called by cfzy_expr_graph_dump() */
+static void expr_dump_node(FILE *f, const struct cfzy_expr *exp, int level)
+{
+       if (exp == NULL)
+               return;
+
+       fprintf(f, "vertice %d %s:%p\n", level, op_print(exp), exp);
+
+       if (exp->left) {
+               fprintf(f, "edge %p %p\n", exp, exp->left);
+               expr_dump_node(f, exp->left, level + 1);
+       }
+
+       if (exp->right) {
+               fprintf(f, "edge %p %p\n", exp, exp->right);
+               expr_dump_node(f, exp->right, level + 1);
+       }
+}
+
+/* dump an expression graph in a file */
+int cfzy_expr_graph_dump(const char *filename, const struct cfzy_expr *exp)
+{
+       FILE *f;
+
+       f = fopen(filename, "w");
+       if (f == NULL)
+               return -1;
+       expr_dump_node(f, exp, 0);
+       fclose(f);
+       return 0;
+}
+
+/* return true if it's a binary operator */
+static int expr_is_bin_op(const struct cfzy_expr *exp)
+{
+       if (exp->optype == OP_AND ||
+           exp->optype == OP_OR ||
+           exp->optype == OP_EQUAL)
+               return 1;
+       return 0;
+}
+
+/*
+ * Dump the expression 'exp' as a string into the buffer 'buf' of
+ * length 'len'. The string is nul-terminated. Return the number of
+ * written bytes on success (not including \0), else return -1.
+ */
+int cfzy_expr_to_str(const struct cfzy_expr *exp, char *buf, int len)
+{
+       int n, orig_len;
+
+       orig_len = len;
+
+       /* dump left expression if it's a binary operator (in case of
+        * unary operator, we only use the right child) */
+       if (expr_is_bin_op(exp)) {
+               n = cfzy_expr_to_str(exp->left, buf, len);
+               if (n == -1 || n >= len || len <= 0)
+                       return -1;
+               buf += n;
+               len -= n;
+       }
+
+       /* dump the operator */
+       if (expr_is_bin_op(exp))
+               n = snprintf(buf, len, " %s ", op_print(exp));
+       else
+               n = snprintf(buf, len, "%s", op_print(exp));
+       if (n == -1 || n >= len || len <= 0)
+               return -1;
+       buf += n;
+       len -= n;
+
+       /* dump the right expression except if it's a variable */
+       if (exp->optype != OP_VAR) {
+               n = cfzy_expr_to_str(exp->right, buf, len);
+               if (n == -1 || n >= len || len <= 0)
+                       return -1;
+               buf += n;
+               len -= n;
+       }
+
+       /* close the bracket if needed */
+       if (exp->optype == OP_OBRACKET) {
+               n = snprintf(buf, len, ")");
+               if (n == -1 || n >= len || len <= 0)
+                       return -1;
+               buf += n;
+               len -= n;
+       }
+
+       return orig_len - len;
+}
+
+/*
+ * Evaluate an expression. Return -1 on error, else the value of the
+ * expression (greater or equal to 0).
+ */
+int cfzy_expr_eval(const struct cfzy_expr *exp,
+                  int (*getvalue)(const char *, void *), void *opaque_arg)
+{
+       switch (exp->optype) {
+       case OP_OR:
+               return cfzy_expr_eval(exp->left, getvalue, opaque_arg) ||
+                       cfzy_expr_eval(exp->right, getvalue, opaque_arg);
+       case OP_AND:
+               return cfzy_expr_eval(exp->left, getvalue, opaque_arg) &&
+                       cfzy_expr_eval(exp->right, getvalue, opaque_arg);
+       case OP_EQUAL:
+               return cfzy_expr_eval(exp->left, getvalue, opaque_arg) ==
+                       cfzy_expr_eval(exp->right, getvalue, opaque_arg);
+       case OP_OBRACKET:
+               return cfzy_expr_eval(exp->right, getvalue, opaque_arg);
+       case OP_NOT:
+               return !cfzy_expr_eval(exp->right, getvalue, opaque_arg);
+       case OP_VAR:
+               /* constants names (used for testing purposes) */
+               if (!strcmp("true", exp->varname))
+                       return 1;
+               if (!strcmp("false", exp->varname))
+                       return 0;
+               if (getvalue == NULL)
+                       return -1;
+               return getvalue(exp->varname, opaque_arg);
+       default:
+               debug_printf("%s(): bad operator\n", __FUNCTION__);
+               return -1;
+       }
+}
+
+/* The argument 'buf' is expression string that must start with an
+ * opening bracket '('. This function returns the len of the
+ * expression contained until we get the closing bracket corresponding
+ * to buf[0]. Return -1 on error. */
+static int get_brac_len(const char *buf)
+{
+       const char *s = buf;
+       int i = 1;
+
+       if (*s != '(')
+               return -1;
+       s++;
+
+       while(*s != '\0' && i != 0) {
+               if (*s == ')')
+                       i--;
+               if (*s == '(')
+                       i++;
+               s++;
+       }
+       if (i != 0)
+               return -1;
+
+       return s-buf;
+}
+
+/*
+ * Allocate a new expression exp corresponding to the next characters
+ * in "buf". On success, a new expression exp is returned and the
+ * number of eaten characters is set in "eatlen". If the end of the
+ * string is reached, the expression exp contains the OP_EOF
+ * operator. On error, NULL is returned.
+ */
+ static struct cfzy_expr *get_next_op(const char *buf, unsigned *eatlen)
+{
+       struct cfzy_expr *exp;
+       const char *s;
+       unsigned len;
+
+       s = buf;
+       exp = malloc(sizeof(struct cfzy_expr));
+       if (exp == NULL)
+               return NULL;
+       memset(exp, 0, sizeof(*exp));
+
+       switch (s[0]) {
+       case '#':
+               exp->optype = OP_EOF;
+               *eatlen = s - buf + 1;
+               return exp;
+       case '\0':
+               exp->optype = OP_EOF;
+               *eatlen = s - buf + 1;
+               return exp;
+       case '(':
+               exp->optype = OP_OBRACKET;
+               *eatlen = s - buf + 1;
+               return exp;
+       case ')':
+               exp->optype = OP_CBRACKET;
+               *eatlen = s - buf + 1;
+               return exp;
+       case '!':
+               exp->optype = OP_NOT;
+               *eatlen = s - buf + 1;
+               return exp;
+       case '&':
+               exp->optype = OP_AND;
+               if (s[1] != '&')
+                       goto fail;
+               *eatlen = s - buf + 2;
+               return exp;
+       case '|':
+               exp->optype = OP_OR;
+               if (s[1] != '|')
+                       goto fail;
+               *eatlen = s - buf + 2;
+               return exp;
+       case '=':
+               if (s[1] != '=')
+                       goto fail;
+               exp->optype = OP_EQUAL;
+               *eatlen = s - buf + 2;
+               return exp;
+       default:
+               /* a variable name must start with a letter */
+               if (isalpha(s[0]))
+                       break;
+               goto fail;
+       }
+
+       /* It's a variable, get name */
+       while (isalnum(s[0]) || s[0] == '_')
+               s++;
+
+       exp->optype = OP_VAR;
+       *eatlen = s - buf;
+
+       /* alloc string for variable name */
+       len = s - buf;
+       exp->varname = malloc(len+1);
+       memcpy(exp->varname, buf, len);
+       exp->varname[len] = '\0';
+       return exp;
+
+ fail:
+       free(exp);
+       return NULL;
+}
+
+/*
+ * Parse the expression contained in s (which is a modifiable
+ * string). Return 0 on success, in this case the expression tree is
+ * returned in exp_ptr. On error, return -1.
+ */
+static int expr_parse(char *s, struct cfzy_expr **exp_ptr)
+{
+       struct cfzy_expr *top = NULL, *exp, *tmp;
+       int len;
+       unsigned eatlen;
+
+       debug_printf("parse <%s>\n", s);
+
+       if (*s == '\0')
+               return -1;
+
+       while (1) {
+
+               /* skip spaces */
+               while (isspace(*s))
+                       s++;
+
+               /* get next operator/operand */
+               exp = get_next_op(s, &eatlen);
+               if (exp == NULL) {
+                       debug_printf("Parse error\n");
+                       goto fail;
+               }
+
+               if (exp->optype == OP_EOF) {
+                       free(exp);
+                       exp = NULL;
+                       break;
+               }
+
+               debug_printf("New node: %s\n", op_print(exp));
+               switch (exp->optype) {
+
+               case OP_OR:
+               case OP_AND:
+               case OP_EQUAL:
+                       /* if it is the first node, error */
+                       if (top == NULL)
+                               goto fail;
+
+                       /* the rightest leaf must be a variable */
+                       tmp = top;
+                       while (tmp->right)
+                               tmp = tmp->right;
+                       if (tmp->optype != OP_VAR)
+                               goto fail;
+
+                       /* if new node priority is lower than root
+                        * node, it becomes the new root */
+                       if (exp->optype < top->optype) {
+                               exp->left = top;
+                               top = exp;
+                               break;
+                       }
+
+                       /* exp is placed at the "rightest" leaf. Check
+                        * that our parent is not a variable */
+                       tmp = top;
+                       while (tmp->right && exp->optype > tmp->right->optype)
+                               tmp = tmp->right;
+
+                       if (tmp->right == NULL)
+                               goto fail;
+
+                       exp->left = tmp->right;
+                       tmp->right = exp;
+                       break;
+
+               case OP_OBRACKET:
+                       /* if operator is an opening bracket,
+                        * recursively call ourself with the
+                        * sub-expression */
+                       len = get_brac_len(s);
+                       if (len < 0) {
+                               debug_printf("Cannot find closing bracket\n");
+                               goto fail;
+                       }
+                       s[len-1] = '\0';
+                       if (expr_parse(s+1, &exp->right) < 0)
+                               goto fail;
+                       if (exp->right == NULL)
+                               goto fail;
+                       s[len-1] = ')';
+                       s += len - 1;
+
+                       /* if it is the first node */
+                       if (top == NULL) {
+                               top = exp;
+                               break;
+                       }
+
+                       /* exp is placed at the "rightest" leaf. Check
+                        * that our parent is not a variable */
+                       tmp = top;
+                       while (tmp->right)
+                               tmp = tmp->right;
+                       if (tmp->optype == OP_VAR)
+                               goto fail;
+                       tmp->right = exp;
+                       break;
+
+               case OP_NOT:
+               case OP_VAR:
+                       /* if it is the first node */
+                       if (top == NULL) {
+                               top = exp;
+                               break;
+                       }
+
+                       /* exp is placed at the "rightest" leaf. Check
+                        * that our parent is not a variable */
+                       tmp = top;
+                       while (tmp->right)
+                               tmp = tmp->right;
+                       if (tmp->optype == OP_VAR)
+                               goto fail;
+                       tmp->right = exp;
+                       break;
+
+               default:
+                       /* should not happen */
+                       debug_printf("Parse error\n");
+                       goto fail;
+               }
+
+               s += eatlen;
+       }
+
+       if (top == NULL)
+               goto fail;
+
+       /* the rightest leaf must be a variable */
+       tmp = top;
+       while (tmp->right)
+               tmp = tmp->right;
+       if (tmp->optype != OP_VAR) {
+               debug_printf("Missing operand\n");
+               goto fail;
+       }
+
+       *exp_ptr = top;
+       return 0;
+
+ fail:
+       if (top != NULL)
+               cfzy_expr_free(top);
+       if (exp != NULL)
+               cfzy_expr_free(exp);
+
+       return -1;
+}
+
+/*
+ * Parse the expression contained in buf. Return an expression tree or
+ * NULL on error.
+ */
+struct cfzy_expr *cfzy_expr_parse(const char *buf)
+{
+       struct cfzy_expr *top;
+       char *s;
+       int ret;
+
+       s = strdup(buf);
+       if (s == NULL)
+               return NULL;
+
+       ret = expr_parse(s, &top);
+       free(s);
+       if (ret < 0)
+               return NULL;
+       return top;
+}
+
+/* Free an expression tree */
+void cfzy_expr_free(struct cfzy_expr *exp)
+{
+       if (exp == NULL)
+               return;
+
+       if (exp->left) {
+               cfzy_expr_free(exp->left);
+               exp->left = NULL;
+       }
+
+       if (exp->right) {
+               cfzy_expr_free(exp->right);
+               exp->right = NULL;
+       }
+
+       if (exp->varname != NULL)
+               free(exp->varname);
+
+       free(exp);
+}
+
+/* recursive function called by cfzy_expr_get_vars() that browse the
+ * expr tree to fill the list */
+static int
+expr_get_vars(const struct cfzy_expr *exp, struct cfzy_list_head *list)
+{
+       struct cfzy_list_elt *e;
+       char *name;
+
+       if (exp->left) {
+               if (expr_get_vars(exp->left, list) < 0)
+                       return -1;
+       }
+
+       if (exp->right) {
+               if (expr_get_vars(exp->right, list) < 0)
+                       return -1;
+       }
+
+       if (exp->optype != OP_VAR)
+               return 0;
+
+       /* ignore true and false constants */
+       if (!strcmp(exp->varname, "true"))
+               return 0;
+       if (!strcmp(exp->varname, "false"))
+               return 0;
+
+       /* nothing to do if var is already in list */
+       TAILQ_FOREACH(e, list, next) {
+               if (!strcmp(e->ptr, exp->varname))
+                       return 0;
+       }
+
+       name = strdup(exp->varname);
+       if (name == NULL)
+               return -1;
+
+       if (cfzy_list_add_tail(list, name) < 0) {
+               free(name);
+               return -1;
+       }
+
+       return 0;
+}
+
+/* Return a list containing all variables on which expression depend */
+struct cfzy_list_head *cfzy_expr_get_vars(const struct cfzy_expr *exp)
+{
+       struct cfzy_list_head *list;
+
+       list = cfzy_list_alloc();
+       if (list == NULL)
+               return NULL;
+
+       if (expr_get_vars(exp, list) < 0) {
+               cfzy_list_free(list, free);
+               return NULL;
+       }
+
+       return list;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_expr.h b/tools/cfzy/libconfizery/cfzy_expr.h
new file mode 100644 (file)
index 0000000..9265955
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CFZY_EXPR_H_
+#define _CFZY_EXPR_H_
+
+/**
+ * @file
+ * Confizery Expression
+ *
+ * This module provides functions to parse an expression string and
+ * convert it in a logical expression tree, that can be evaluated,
+ * according to the variables defined in the configuration tree.
+ *
+ * It is used in configuration tree file, for instance in the "if"
+ * node or the "default" attribute.
+ */
+
+#include <sys/queue.h>
+
+#include "cfzy_list.h"
+
+/**
+ * Type of operand in expression tree (from low to high priority)
+ */
+enum cfzy_expr_op {
+       OP_OR,
+       OP_AND,
+       OP_EQUAL,
+       OP_OBRACKET,
+       OP_CBRACKET,
+       OP_NOT,
+       OP_VAR,
+       OP_EOF,
+};
+
+/**
+ * Structure describing an expression tree
+ *
+ * An expression tree is a binary tree. Each node contains an
+ * operator, or a variable name. If it's a unary operator, only the
+ * right child is used, else both left and right children are set.
+ */
+struct cfzy_expr {
+       TAILQ_ENTRY(expr) next;   /**< next entry in list */
+       enum cfzy_expr_op optype; /**< operator type */
+       char *varname;            /**< var name, only valid if type is OP_VAR */
+       struct cfzy_expr *left;   /**< left child */
+       struct cfzy_expr *right;  /**< right child */
+};
+
+/**
+ * Parse a string and convert it in an expression tree
+ *
+ * @param buf
+ *   String containing an expression
+ * @return
+ *   Expression tree, or NULL on error
+ */
+struct cfzy_expr *cfzy_expr_parse(const char *buf);
+
+/**
+ * Free an expression tree previously allocated with cfzy_expr_parse()
+ *
+ * @param exp
+ *   Expression to free
+ */
+void cfzy_expr_free(struct cfzy_expr *exp);
+
+/**
+ * Write an expression tree in a string buffer
+ *
+ * Dump the expression 'exp' as a string into the buffer 'buf' of
+ * length 'len'. The output string is always nul-terminated.
+ *
+ * @param exp
+ *   Expression tree to dump
+ * @param buf
+ *   Destination buffer
+ * @param len
+ *   Len of the destination buffer
+ * @return
+ *   Number of written bytes on success (not including '\0'), else
+ *   return -1.
+ */
+int cfzy_expr_to_str(const struct cfzy_expr *exp, char *buf, int len);
+
+/**
+ * Evaluate an expression tree
+ *
+ * Evaluate the given expression tree according to variables defined
+ * in conftree.
+ *
+ * @param exp
+ *   Expression tree to evaluate
+ * @param getvalue
+ *   Function used to evaluate a variable. This function should
+ *   return the value of the variable (positive), or a negative value
+ *   on error (variable invalid or not found).
+ *   This argument can be NULL, in this case only "true" and "false" are
+ *   evaluated.
+ * @param opaque_arg
+ *   Second argument given as-is to getvalue() function.
+ * @return
+ *   The value of the expression (greater or equal to 0), or -1 on
+ *   error
+ */
+int cfzy_expr_eval(const struct cfzy_expr *exp,
+                  int (*getvalue)(const char *, void *), void *opaque_arg);
+
+/**
+ * Dump the expression graph in a file
+ *
+ * Useful for debug purpose only. The output can be parsed with
+ * cfzy_expr_graph.py.
+ *
+ * @param filename
+ *   Name of file
+ * @param exp
+ *   Expression tree to dump
+ * @return
+ *   0 on succes, -1 on error
+ */
+int cfzy_expr_graph_dump(const char *filename, const struct cfzy_expr *exp);
+
+/**
+ * Return a list containing all variables on which expression depend
+ *
+ * Allocate a list head, and for each variable of the expression,
+ * allocate an element and add it in the list. Each element points to
+ * a string which is allocated and should be freed by the user. Each
+ * variable is present only once in the list.
+ *
+ * @param exp
+ *   Expression tree
+ * @return
+ *   List containing the variables (it can be empty), or NULL on error.
+ */
+struct cfzy_list_head *cfzy_expr_get_vars(const struct cfzy_expr *exp);
+
+#endif /* _CFZY_EXPR_H_ */
diff --git a/tools/cfzy/libconfizery/cfzy_expr_graph.py b/tools/cfzy/libconfizery/cfzy_expr_graph.py
new file mode 100644 (file)
index 0000000..b56bbd8
--- /dev/null
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+
+import matplotlib.pyplot as plt
+import networkx as nx
+import sys, re, random
+
+G = nx.Graph()
+labels = {}
+levels = {}
+init_pos = {}
+maxlevel = 0
+
+# always keep the same seed
+random.seed(1234)
+
+# parse lines, new vertice is:
+#   vertice LEVEL OPERATOR:VERTICE_NAME
+# new edge is:
+#   edge OPERATOR:VERTICE_NAME OPERATOR:VERTICE_NAME
+while True:
+    l = sys.stdin.readline()
+    if l == "":
+        break
+    m = re.match("^vertice ([0-9]+) ([^\s]+)$", l)
+    if m:
+        level = m.groups()[0]
+        level = int(level)
+        operator, name = m.groups()[1].split(":")
+        print "vert: operator = %s, level = %d, name = %s"%(operator, level, name)
+        labels[name] = operator
+        init_pos[name] = (random.random(), -level)
+        levels[name] = level
+        if level > maxlevel:
+            maxlevel = level
+        continue
+    m = re.match("^edge ([^\s]+) ([^\s]+)$", l)
+    if m:
+        vertice1, vertice2 = m.groups()
+        G.add_edge(vertice1, vertice2)
+        continue
+
+pos = nx.spring_layout(G, pos=init_pos)
+
+# root node is blue and bigger
+rootnode = filter(lambda x:levels[x] == 0, levels.keys())
+nx.draw_networkx_nodes(G, pos, node_color='b', node_size=2000, nodelist=rootnode)
+
+# graph other nodes
+for level in range(1, maxlevel + 1):
+    othernodes = filter(lambda x:levels[x] == level, levels.keys())
+    color = (1., float(level)/maxlevel, float(level)/maxlevel)
+    color = [color] * len(othernodes)
+    nx.draw_networkx_nodes(G, pos, node_color=color, node_size=1000, nodelist=othernodes)
+
+nx.draw_networkx_edges(G, pos)
+nx.draw_networkx_labels(G, pos, labels, font_size=20, font_family='sans-serif')
+
+plt.axis('off')
+plt.savefig("weighted_graph.png") # save as png
+plt.show() # display
diff --git a/tools/cfzy/libconfizery/cfzy_file.c b/tools/cfzy/libconfizery/cfzy_file.c
new file mode 100644 (file)
index 0000000..4d80081
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "cfzy_log.h"
+
+#define LOG(level, fmt, args...)                               \
+       CFZY_LOG("file", level, fmt, ##args)
+
+/* 0 = same file, 1 = different files, -1 = error
+ * the function does not close the files */
+int cfzy_file_compare(FILE *f1, FILE *f2)
+{
+       char buf1[BUFSIZ], buf2[BUFSIZ];
+       size_t ret1, ret2;
+
+       rewind(f1);
+       rewind(f2);
+
+       while (1) {
+               ret1 = fread(buf1, 1, sizeof(buf1), f1);
+               if (ret1 == 0 && ferror(f1))
+                       return -1;
+
+               ret2 = fread(buf2, 1, sizeof(buf2), f2);
+               if (ret2 == 0 && ferror(f2))
+                       return -1;
+
+               if (ret1 != ret2)
+                       return 1;
+
+               if (ret1 == 0) /* implies ret2 == 0 too */
+                       return 0;
+
+               if (memcmp(buf1, buf2, ret1))
+                       return 1;
+       }
+
+       /* never reached */
+       return 0;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_file.h b/tools/cfzy/libconfizery/cfzy_file.h
new file mode 100644 (file)
index 0000000..c9ed80b
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * File-related tools
+ */
+
+#ifndef CFZY_FILE_H
+#define CFZY_FILE_H
+
+#include <stdio.h>
+
+/**
+ * Compare 2 files
+ *
+ * The function rewind to the beginning of the files, then compares
+ * them. The file position indicator is undefined after the execution
+ * of the function, and the files are not closed.
+ *
+ * @param f1
+ *   pointer to first stream
+ * @param f2
+ *   pointer to second stream
+ * @return
+ *   0 if files are the same, 1 if files are different, -1 on error
+ */
+int cfzy_file_compare(FILE *f1, FILE *f2);
+
+#endif /* CFZY_FILE */
diff --git a/tools/cfzy/libconfizery/cfzy_htable.c b/tools/cfzy/libconfizery/cfzy_htable.c
new file mode 100644 (file)
index 0000000..0718f8a
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/queue.h>
+
+#include "cfzy_htable.h"
+
+/* return the hash from name */
+static uint32_t hash_name(const char *name)
+{
+       uint32_t h = 0;
+       const unsigned char *c;
+
+       /* XXX add murmurhash ? */
+       for (c = (const unsigned char *)name; *c; c++)
+               h += ((((uint32_t)*c) << 4) + (((uint32_t)*c) >> 4)) * 11;
+
+       return h & CFZY_HTABLE_MASK;
+}
+
+/* return the hlist_elt associated to the given name. If not found,
+ * return NULL. */
+static struct cfzy_hlist_elt *
+__cfzy_htable_lookup(const struct cfzy_htable *htable, const char *name)
+{
+       struct cfzy_hlist_elt *e;
+       uint32_t h = hash_name(name);
+
+       LIST_FOREACH(e, &htable->buckets[h], next) {
+               if (!strcmp(name, e->name))
+                       break;
+       }
+       return e;
+}
+
+/* return the object associated to the given name. If not found,
+ * return NULL. */
+void *cfzy_htable_lookup(const struct cfzy_htable *htable, const char *name)
+{
+       struct cfzy_hlist_elt *e = __cfzy_htable_lookup(htable, name);
+       if (e == NULL)
+               return NULL;
+       return e->ptr;
+}
+
+/* add an entry in the htable, allocating a new hlist_elt */
+int cfzy_htable_add(struct cfzy_htable *htable, const char *name,
+                   void *ptr)
+{
+       struct cfzy_hlist_elt *e;
+       uint32_t h;
+
+       /* The ptr should not be NULL, else we won't differentiate a
+        * lookup matching a NULL element and a non-matching one */
+       if (ptr == NULL)
+               return -1;
+
+       e = malloc(sizeof(struct cfzy_hlist_elt));
+       if (e == NULL)
+               return -1;
+
+       e->name = strdup(name);
+       if (e->name == NULL) {
+               free(e);
+               return -1;
+       }
+
+       e->ptr = ptr;
+       h = hash_name(name);
+       LIST_INSERT_HEAD(&htable->buckets[h], e, next);
+
+       return 0;
+}
+
+/* delete and free an hlist_elt from the htable */
+static int __cfzy_htable_del(struct cfzy_hlist_elt *e)
+{
+       LIST_REMOVE(e, next);
+       free(e->name);
+       free(e);
+       return 0;
+}
+
+/* delete an entry from the htable, freeing the hlist_elt */
+int cfzy_htable_del(struct cfzy_htable *htable, const char *name)
+{
+       struct cfzy_hlist_elt *e = __cfzy_htable_lookup(htable, name);
+
+       if (e == NULL)
+               return -1;
+
+       return __cfzy_htable_del(e);
+}
+
+/* allocate and initialize a new htable */
+struct cfzy_htable *cfzy_htable_alloc(void)
+{
+       struct cfzy_htable *htable;
+       unsigned i;
+
+       htable = malloc(sizeof(struct cfzy_htable));
+       if (htable == NULL)
+               return NULL;
+
+       for (i = 0; i < CFZY_HTABLE_SIZE; i++) {
+               LIST_INIT(&htable->buckets[i]);
+       }
+       return htable;
+}
+
+/* empty the htable, freeing hlist_elt structures, and free the htable */
+void cfzy_htable_free(struct cfzy_htable *htable, void (*free_fct)(void *))
+{
+       struct cfzy_hlist_elt *e;
+       unsigned i;
+
+       for (i = 0; i < CFZY_HTABLE_SIZE; i++) {
+               while ((e = LIST_FIRST(&htable->buckets[i])) != NULL) {
+                       if (free_fct != NULL)
+                               free_fct(e->ptr);
+
+                       __cfzy_htable_del(e);
+               }
+       }
+       free(htable);
+}
diff --git a/tools/cfzy/libconfizery/cfzy_htable.h b/tools/cfzy/libconfizery/cfzy_htable.h
new file mode 100644 (file)
index 0000000..21e9de3
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CFZY_HTABLE_H_
+#define _CFZY_HTABLE_H_
+
+/**
+ * @file
+ * Confizery Hash Table
+ *
+ * This module is a simple hash table implementation, based on LIST
+ * from sys/queue.h --see queue(3)-- and with a constant number of
+ * buckets. It handles the allocation of chaining structure so any
+ * kind of element can be added/deleted. Each element is referenced by
+ * a string (key).
+ */
+
+#include <sys/queue.h>
+
+/* we use a constant size of htable, it simplifies the code and it is
+ * enough for our purpose */
+
+#define CFZY_HTABLE_ORDER 10
+#define CFZY_HTABLE_SIZE  (1 << CFZY_HTABLE_ORDER)
+#define CFZY_HTABLE_MASK  (CFZY_HTABLE_SIZE - 1)
+
+/**
+ * A hash table bucket
+ */
+LIST_HEAD(cfzy_hlist_head, cfzy_hlist_elt);
+
+/**
+ * A hash table element, used to chain objects together and store the
+ * name (key). This structure is allocated by cfzy_htable_add().
+ */
+struct cfzy_hlist_elt {
+       LIST_ENTRY(cfzy_hlist_elt) next;
+       char *name;
+       void *ptr;
+};
+
+/**
+ * A hash table structure
+ */
+struct cfzy_htable {
+       struct cfzy_hlist_head buckets[CFZY_HTABLE_SIZE];
+};
+
+/**
+ * Lookup for an element matching the given key
+ *
+ * @param htable
+ *   Hash table pointer
+ * @param name
+ *   String identifying the element (key)
+ * @return
+ *   Pointer to the element, or NULL on error
+ */
+void *cfzy_htable_lookup(const struct cfzy_htable *htable, const char *name);
+
+
+/**
+ * Add an element in the hash table
+ *
+ * Allocate a new cfzy_hlist_elt structure, initialize it to point to
+ * the given pointer "ptr", then queue the structure in the
+ * appropriate hash table bucket.
+ *
+ * @param htable
+ *   Hash table pointer
+ * @param name
+ *   String identifying the element (key)
+ * @param ptr
+ *   Pointer to the element (must not be NULL)
+ * @return
+ *   0 on success, -1 on error
+ */
+int cfzy_htable_add(struct cfzy_htable *htable, const char *name,
+                   void *ptr);
+
+/**
+ * Remove an element from the hash table
+ *
+ * Unchain the structure from the hash table bucket, and free the
+ * cfzy_hlist_elt structure.
+ *
+ * @param htable
+ *   Hash table pointer
+ * @param name
+ *   String identifying the element (key)
+ * @return
+ *   0 on success, -1 on error (element not found)
+ */
+int cfzy_htable_del(struct cfzy_htable *htable, const char *name);
+
+/**
+ * Allocate and initialize a new empty htable
+ *
+ * @return
+ *   Hash table pointer or NULL on error
+ */
+struct cfzy_htable *cfzy_htable_alloc(void);
+
+/**
+ * Free a htable
+ *
+ * Empty the htable, freeing all elements (cfzy_hlist_elt structures,
+ * and its pointer, using the provided free_fct), then free the
+ * cfzy_htable.
+ *
+ * @param htable
+ *   Hash table pointer
+ * @param free_fct
+ *   Function to free an element
+ */
+void cfzy_htable_free(struct cfzy_htable *htable, void (*free_fct)(void *));
+
+#endif /* _CFZY_HTABLE_H_ */
diff --git a/tools/cfzy/libconfizery/cfzy_list.c b/tools/cfzy/libconfizery/cfzy_list.c
new file mode 100644 (file)
index 0000000..f7a7019
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+
+#include "cfzy_list.h"
+
+/* allocate a list_elt and insert at the tail of the list */
+int cfzy_list_add_tail(struct cfzy_list_head *list, void *ptr)
+{
+       struct cfzy_list_elt *e;
+
+       e = malloc(sizeof(struct cfzy_list_elt));
+       if (e == NULL)
+               return -1;
+
+       e->ptr = ptr;
+       TAILQ_INSERT_TAIL(list, e, next);
+       return 0;
+}
+
+/* allocate a list_elt and insert at the tail of the list */
+int cfzy_list_add_head(struct cfzy_list_head *list, void *ptr)
+{
+       struct cfzy_list_elt *e;
+
+       e = malloc(sizeof(struct cfzy_list_elt));
+       if (e == NULL)
+               return -1;
+
+       e->ptr = ptr;
+       TAILQ_INSERT_HEAD(list, e, next);
+       return 0;
+}
+
+/* remove from the list and free the list_elt */
+void cfzy_list_del(struct cfzy_list_head *list, struct cfzy_list_elt *e)
+{
+       TAILQ_REMOVE(list, e, next);
+       free(e);
+}
+
+/* check if an elt is in list */
+int cfzy_list_elt_is_in_list(const struct cfzy_list_head *list, const void *ptr)
+{
+       struct cfzy_list_elt *e;
+
+       TAILQ_FOREACH(e, list, next) {
+               if (e->ptr == ptr)
+                       return 1;
+       }
+       return 0;
+}
+
+/* allocate a new list_head */
+struct cfzy_list_head *cfzy_list_alloc(void)
+{
+       struct cfzy_list_head *list;
+
+       list = malloc(sizeof(struct cfzy_list_head));
+       if (list == NULL)
+               return NULL;
+
+       TAILQ_INIT(list);
+       return list;
+}
+
+/* empty the list, freeing the list_elt, and free the list_head */
+void cfzy_list_free(struct cfzy_list_head *list, void (*free_fct)(void *))
+{
+       struct cfzy_list_elt *e;
+
+       while ((e = TAILQ_FIRST(list)) != NULL) {
+               TAILQ_REMOVE(list, e, next);
+               if (free_fct != NULL)
+                       free_fct(e->ptr);
+               free(e);
+       }
+       free(list);
+}
diff --git a/tools/cfzy/libconfizery/cfzy_list.h b/tools/cfzy/libconfizery/cfzy_list.h
new file mode 100644 (file)
index 0000000..48b8d52
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CFZY_LIST_H_
+#define _CFZY_LIST_H_
+
+/**
+ * @file
+ * Confizery List
+ *
+ * This module is a list implementation, based on TAILQ from
+ * sys/queue.h -see queue(3)-. It handles the allocation of chaining
+ * structure so any kind of element can be chained.
+ *
+ * To be really useful, this API must be used in conjonction with the
+ * TAILQ_* macros. For instance, to browse the list:
+ *
+ *     struct cfzy_list_elt *e;
+ *     TAILQ_FOREACH(e, list_head, next) {
+ *             obj = e->ptr;
+ *             ...
+ *     }
+ */
+
+#include <sys/queue.h>
+
+/**
+ * A list head
+ */
+TAILQ_HEAD(cfzy_list_head, cfzy_list_elt);
+
+/**
+ * A list element, used to chain objects together. This structure is
+ * allocated by cfzy_list_add().
+ */
+struct cfzy_list_elt {
+       TAILQ_ENTRY(cfzy_list_elt) next;
+       void *ptr;
+};
+
+/**
+ * Insert an element at the tail of the list
+ *
+ * Allocate a cfzy_list_elt structure, set its ptr field and insert it
+ * at the tail of the list.
+ *
+ * @param list
+ *   List head to insert the element
+ * @param ptr
+ *   Pointer to the object that will be referenced by list_elt->ptr
+ * @return
+ *   0 on succes, and negative on error
+ */
+int cfzy_list_add_tail(struct cfzy_list_head *list, void *ptr);
+
+/**
+ * Insert an element at the head of the list
+ *
+ * Allocate a cfzy_list_elt structure, set its ptr field and insert it
+ * at the head of the list.
+ *
+ * @param list
+ *   List head to insert the element
+ * @param ptr
+ *   Pointer to the object that will be referenced by list_elt->ptr
+ * @return
+ *   0 on succes, and negative on error
+ */
+int cfzy_list_add_head(struct cfzy_list_head *list, void *ptr);
+
+/**
+ * Remove an element from the list
+ *
+ * Unchain the cfzy_list_elt structure and free it.
+ *
+ * @param list
+ *   List head to insert the element
+ * @param elt
+ *   List element
+ */
+void cfzy_list_del(struct cfzy_list_head *list, struct cfzy_list_elt *elt);
+
+/**
+ * Check if an element is in the list
+ *
+ * Browse the list and check if there is already an cfzy_list_elt
+ * structure referencing ptr.
+ *
+ * @param list
+ *   List head
+ * @param ptr
+ *   Pointer to the element
+ * @return
+ *   1 if the element is in list, else 0
+ */
+int cfzy_list_elt_is_in_list(const struct cfzy_list_head *list, const void *ptr);
+
+/**
+ * Allocate a new list
+ *
+ * @return
+ *   Pointer to the new list head, or NULL on error
+ */
+struct cfzy_list_head *cfzy_list_alloc(void);
+
+/**
+ * Free a list
+ *
+ * Free all cfzy_list_elt of the list using the free_fct if provided,
+ * then free the cfzy_list_head structure.
+ *
+ * @param list
+ *   List head pointer
+ * @param free_fct
+ *   Function to free an element
+ */
+void cfzy_list_free(struct cfzy_list_head *list, void (*free_fct)(void *));
+
+#endif /* _CFZY_LIST_H_ */
diff --git a/tools/cfzy/libconfizery/cfzy_log.c b/tools/cfzy/libconfizery/cfzy_log.c
new file mode 100644 (file)
index 0000000..ef143b8
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this log of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this log of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "cfzy_log.h"
+#include "cfzy_htable.h"
+
+static struct cfzy_htable *level_htable = NULL;
+static int default_level = CFZY_LOG_NOTICE;
+
+/* initialize log susbsystem */
+int cfzy_log_init(void)
+{
+       level_htable = cfzy_htable_alloc();
+       if (level_htable == NULL)
+               return -1;
+       return 0;
+}
+
+/* free all structures associated to log subsystem */
+void cfzy_log_exit(void)
+{
+       cfzy_htable_free(level_htable, free);
+}
+
+/* set default log level (for log types that are not registered) */
+int cfzy_log_set_default_level(int level)
+{
+       if (level != -1 && (level < CFZY_LOG_ERR || level > CFZY_LOG_DEBUG))
+               return -1;
+       default_level = level;
+       return 0;
+}
+
+/* set log level for a specific log type: the string is added in the
+ * hash table */
+int cfzy_log_set_level(const char *logtype, int level)
+{
+       int *l;
+
+       if (level != -1 && (level < CFZY_LOG_ERR || level > CFZY_LOG_DEBUG))
+               return -1;
+       l = cfzy_htable_lookup(level_htable, logtype);
+
+       /* if log is already registered, it's easy */
+       if (l != NULL) {
+               *l = level;
+               return 0;
+       }
+
+       /* else allocate a new (int *) and store it in the htable */
+       l = malloc(sizeof(*l));
+       if (l == NULL)
+               return -1;
+
+       *l = level;
+       cfzy_htable_add(level_htable, logtype, l);
+       return 0;
+}
+
+/* print a log if given level is <= to configured log level */
+int cfzy_log_printf(const char *logtype, int level, const char *fmt, ...)
+{
+       int *l;
+       va_list ap;
+       int ret;
+
+       l = cfzy_htable_lookup(level_htable, logtype);
+
+       if (l == NULL && level > default_level)
+               return 0;
+       else if (l != NULL && level > *l)
+               return 0;
+
+       va_start(ap, fmt);
+       ret = vfprintf(stderr, fmt, ap);
+       va_end(ap);
+
+       return ret;
+}
diff --git a/tools/cfzy/libconfizery/cfzy_log.h b/tools/cfzy/libconfizery/cfzy_log.h
new file mode 100644 (file)
index 0000000..a4b0f91
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this log of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this log of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CFZY_LOG_H_
+#define _CFZY_LOG_H_
+
+/**
+ * @file
+ * Confizery Log
+ *
+ * This module provides logging helpers. XXX explain
+ */
+
+/* log levels */
+#define CFZY_LOG_ERR      0
+#define CFZY_LOG_WARNING  1
+#define CFZY_LOG_NOTICE   2
+#define CFZY_LOG_INFO     3
+#define CFZY_LOG_DEBUG    4
+
+/**
+ * Initialize log subsystem
+ *
+ * @return
+ *   0 on succes, -1 on error
+ */
+int cfzy_log_init(void);
+
+/**
+ * Uninitialize log subsystem
+ */
+void cfzy_log_exit(void);
+
+/**
+ * Set default log level
+ *
+ * @param level
+ *   Level of log, -1 means no log at all
+ * @return
+ *   0 on succes, -1 on error
+ */
+int cfzy_log_set_default_level(int level);
+
+/**
+ * Set log level for a specific log type
+ *
+ * @param logtype
+ *   String describing the log type
+ * @param level
+ *   Level of log, -1 means no log at all
+ * @return
+ *   0 on succes, -1 on error
+ */
+int cfzy_log_set_level(const char *logtype, int level);
+
+/**
+ * Print a log if given level is <= to configured log level
+ *
+ * The log is displayed on standard error.
+ *
+ * @param logtype
+ *   String describing the log type
+ * @param level
+ *   Level at which the text must be displayed
+ * @param fmt
+ *   Format string, followed by optional arguments, like in printf.
+ * @return
+ *   The number of written characters (0 if nothing is diplayed),
+ *   or -1 on error.
+ */
+int cfzy_log_printf(const char *logtype, int level, const char *fmt, ...);
+
+#if 0
+/**
+ * Print a log if given level is <= to configured log level
+ */
+#define CFZY_LOG(logtype, level, fmt, args...)                         \
+       cfzy_log_printf(logtype, CFZY_LOG_##level, fmt,                 \
+                       ## args)
+#else
+#define CFZY_LOG(logtype, level, fmt, args...)                         \
+       cfzy_log_printf(logtype, CFZY_LOG_##level, "%s():%d " fmt,      \
+                       __func__, __LINE__, ## args)
+#endif
+
+#endif /* _CFZY_LOG_H_ */
diff --git a/tools/cfzy/libconfizery/cfzy_string.c b/tools/cfzy/libconfizery/cfzy_string.c
new file mode 100644 (file)
index 0000000..ebdafbf
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "cfzy_log.h"
+
+#define LOG(level, fmt, args...)                               \
+       CFZY_LOG("string", level, fmt, ##args)
+
+/* Acts as a asprintf, except that it appends data after buf (if not
+ * NULL), reallocating memory if necessary. */
+int cfzy_string_asprintf(char **buf, const char *fmt, ...)
+{
+       va_list ap;
+       char dummy;
+       int offset = 0, buflen, ret;
+
+       if (*buf != NULL)
+               offset = strlen(*buf);
+
+       va_start(ap, fmt);
+       ret = vsnprintf(&dummy, 1, fmt, ap);
+       va_end(ap);
+       if (ret < 0) {
+               LOG(ERR, "first vsnprintf failed\n");
+               return ret;
+       }
+
+       buflen = offset + ret + 1;
+       *buf = realloc(*buf, buflen);
+       if (*buf == NULL) {
+               LOG(ERR, "asprintf: not enough memory\n");
+               return -1;
+       }
+
+       va_start(ap, fmt);
+       ret = vsnprintf(*buf + offset, buflen - offset, fmt, ap);
+       va_end(ap);
+
+       if (ret >= buflen || ret < 0) {
+               free(*buf);
+               *buf = NULL;
+               LOG(ERR, "second vsnprintf failed\n");
+               return -1;
+       }
+
+       return ret;
+}
+
+/*
+ * If the buffer is surrounded by quotes (simple or double), this
+ * function will allocate a new string and remove the quotes properly,
+ * also deleting the backslash in occurences of \\, \" or \'. If the
+ * buffer does not start by a quote, the function just return a
+ * duplicate of the input string. On sucess, the eatlen is updated to
+ * the number of consumed bytes (including quotes if any, but not
+ * including \0). On error, return NULL, in this case *eatlen is
+ * undefined.
+ */
+char *cfzy_string_unquote(const char *buf, unsigned *eatlen)
+{
+       char delim;
+       char *out, *s2;
+       const char *s;
+
+       s = buf;
+       out = strdup(s);
+       if (out == NULL) {
+               LOG(ERR, "not enough memory\n");
+               return NULL;
+       }
+
+       if (s[0] == '"')
+               delim = '"';
+       else if (s[0] == '\'')
+               delim = '\'';
+       else {
+               *eatlen = strlen(s);
+               return out;
+       }
+       s++;
+
+       s2 = out;
+       while (*s != delim) {
+               if (*s == '\0') {
+                       free(out);
+                       return NULL;
+               }
+               if (*s == '\\' && *(s+1) == delim) {
+                       *s2 = delim;
+                       s += 2;
+                       s2 ++;
+                       continue;
+               }
+               if (*s == '\\' && *(s+1) == '\\') {
+                       *s2 = '\\';
+                       s += 2;
+                       s2 ++;
+                       continue;
+               }
+               *s2 = *s;
+               s ++;
+               s2 ++;
+       }
+       *s2 = '\0';
+
+       *eatlen = s - buf + 1;
+       return out;
+}
+
+/* quote a string and escape original quotes */
+char *cfzy_string_quote(const char *src)
+{
+       int s, d;
+       char *dst;
+
+       /* get dst buf len */
+       for (s = 0, d = 0; src[s] != '\0'; s++, d++) {
+               if (src[s] == '"')
+                       d++;
+               if (src[s] == '\\' && src[s+1] == '"')
+                       d++;
+       }
+
+       dst = malloc(d + 3); /* 3 for the 2 quotes and the \0 */
+       if (dst == NULL)
+               return NULL;
+
+       dst[0] = '"';
+       for (s = 0, d = 1; src[s] != '\0'; s++, d++) {
+               if (src[s] == '"')
+                       dst[d++] = '\\';
+               if (src[s] == '\\' && src[s+1] == '"')
+                       dst[d++] = '\\';
+
+               dst[d] = src[s];
+       }
+
+       dst[d++] = '"';
+       dst[d++] = '\0';
+
+       return dst;
+}
+
diff --git a/tools/cfzy/libconfizery/cfzy_string.h b/tools/cfzy/libconfizery/cfzy_string.h
new file mode 100644 (file)
index 0000000..6514be0
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * String-related tools
+ */
+
+#ifndef CFZY_STRING_H
+#define CFZY_STRING_H
+
+/**
+ * Append a formatted string in an allocated buffer
+ *
+ * This function acts as a asprintf, with some differences:
+ * - *but can already contain a string, in this case, the new string
+ *   is appended.
+ * - *strp is set to NULL on error, like in BSD's asprintf
+ *
+ * @param strp
+ *   Pointer to a (char *), must not be NULL. If *strp is NULL a new
+ *   buffer is allocated to store the string. Else, if *strp is not
+ *   NULL, it must point to a valid string in a buffer that was
+ *   previously allocated using malloc.
+ * @param fmt
+ *   Format string, followed by other arguments, like in printf
+ * @return
+ *   The number of bytes printed. If memory allocation wasn't
+ *   possible, or some other error occurs, these functions will return
+ *   -1, and the contents of strp is set to NULL.
+ **/
+int cfzy_string_asprintf(char **buf, const char *fmt, ...);
+
+/**
+ * Remove quotes on a string
+ *
+ * If the buffer is surrounded by quotes (simple or double), this
+ * function will allocate a new string and remove the quotes properly,
+ * also deleting the backslash in occurences of \\, \" or \'. If the
+ * buffer does not start by a quote, the function just return a
+ * duplicate of the input string.
+ *
+ * On sucess, the value pointer by eatlen is updated to the len of
+ * consumed bytes in the input buffer, including quotes if any, but
+ * not including \0. On error *eatlen is undefined.
+ *
+ * @param buf
+ *   string buffer
+ * @param eatlen
+ *   pointer to an int where the number of consumed bytes is written
+ * @return
+ *   the unquoted string (allocated), or NULL on error
+ */
+char *cfzy_string_unquote(const char *buf, unsigned *eatlen);
+
+/**
+ * Quote a string with double quotes
+ *
+ * Allocate a new string which is a copy of the first one, surrounded
+ * by quotes and quotes of initial string are escaped with a
+ * backslash.
+ *
+ * @param src
+ *   input string
+ * @return
+ *   the quoted string (allocated), or NULL on error
+ */
+char *cfzy_string_quote(const char *src);
+
+#endif /* CFZY_STRING */