From bed730e2452ea67b4d9dd6180e03ca0ad437f2c7 Mon Sep 17 00:00:00 2001 From: eddyem Date: Fri, 7 Dec 2018 03:04:18 +0300 Subject: [PATCH] start gathering snippets into one library --- .gitignore | 39 +- CMake.readme | 4 + CMakeLists.txt | 159 ++++++++ daemon.c | 147 +++++++ examples/CMakeLists.txt | 10 + examples/cmdlnopts.c | 87 +++++ examples/cmdlnopts.h | 39 ++ examples/helloworld.c | 35 ++ examples/options.c | 81 ++++ locale/ru/LC_MESSAGES/usefull_macros.mo | Bin 0 -> 1926 bytes locale/ru/ru.po | 134 +++++++ parseargs.c | 495 ++++++++++++++++++++++++ usefull_macros.c | 492 +++++++++++++++++++++++ usefull_macros.h | 254 ++++++++++++ usefull_macros.pc.in | 10 + 15 files changed, 1948 insertions(+), 38 deletions(-) create mode 100644 CMake.readme create mode 100644 CMakeLists.txt create mode 100644 daemon.c create mode 100644 examples/CMakeLists.txt create mode 100644 examples/cmdlnopts.c create mode 100644 examples/cmdlnopts.h create mode 100644 examples/helloworld.c create mode 100644 examples/options.c create mode 100644 locale/ru/LC_MESSAGES/usefull_macros.mo create mode 100644 locale/ru/ru.po create mode 100644 parseargs.c create mode 100644 usefull_macros.c create mode 100644 usefull_macros.h create mode 100644 usefull_macros.pc.in diff --git a/.gitignore b/.gitignore index c6127b3..e83a4ee 100644 --- a/.gitignore +++ b/.gitignore @@ -3,18 +3,6 @@ # Object files *.o -*.ko -*.obj -*.elf - -# Linker output -*.ilk -*.map -*.exp - -# Precompiled Headers -*.gch -*.pch # Libraries *.lib @@ -22,31 +10,6 @@ *.la *.lo -# Shared objects (inc. Windows DLLs) -*.dll +# Shared objects *.so *.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ -*.su -*.idb -*.pdb - -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf diff --git a/CMake.readme b/CMake.readme new file mode 100644 index 0000000..e7c8ccd --- /dev/null +++ b/CMake.readme @@ -0,0 +1,4 @@ +cmake defines: +-DDEBUG=1 - debug mode +-DNOGETTEXT=1 - don't run xgettext +-DEXAMPLES=1 - to compile examples diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9bac400 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,159 @@ +cmake_minimum_required(VERSION 3.9) +set(PROJ usefull_macros) +set(MINOR_VERSION "1") +set(MID_VERSION "0") +set(MAJOR_VERSION "0") +set(PROJ_VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}") + +project(${PROJ} VERSION ${PROJ_VERSION} LANGUAGES C) + +# default flags +set(CMAKE_C_FLAGS "${CFLAGS} -O2") +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS}") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W") +set(CMAKE_COLOR_MAKEFILE ON) + +# threads number definition +if(NOT DEFINED PROCESSOR_COUNT) + set(PROCESSOR_COUNT 2) # by default 2 cores + set(cpuinfo_file "/proc/cpuinfo") + if(EXISTS "${cpuinfo_file}") + file(STRINGS "${cpuinfo_file}" procs REGEX "^processor.: [0-9]+$") + list(LENGTH procs PROCESSOR_COUNT) + endif() +endif() +add_definitions(-DTHREAD_NUMBER=${PROCESSOR_COUNT}) +message("In multithreaded operations will use ${PROCESSOR_COUNT} threads") + +# cmake -DDEBUG=1 -> debugging +if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + if(DEFINED DEBUG AND DEBUG EQUAL 1) + set(CMAKE_BUILD_TYPE "Debug") + else() + set(CMAKE_BUILD_TYPE "Release") + endif() +endif() + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_definitions(-DEBUG) + set(CMAKE_VERBOSE_MAKEFILE true) + if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + message("install to ${CMAKE_CURRENT_SOURCE_DIR}/install ") + set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/install) + endif() + set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS_DEBUG}) +else() + set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS_RELEASE}) +endif() + +message("Build type: ${CMAKE_BUILD_TYPE}") + +# here is one of two variants: all .c in directory or .c files in list +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES) + +# directory should contain dir locale/ru for gettext translations +set(LCPATH ${CMAKE_SOURCE_DIR}/locale/ru) +if(NOT DEFINED LOCALEDIR) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(LOCALEDIR ${CMAKE_CURRENT_SOURCE_DIR}/locale) + else() + set(LOCALEDIR ${CMAKE_INSTALL_PREFIX}/share/locale) + endif() +endif() + +###### pkgconfig ###### +# pkg-config modules (for pkg-check-modules) +#set(MODULES cfitsio fftw3) +# find packages: +#find_package(PkgConfig REQUIRED) +#pkg_check_modules(${PROJ} REQUIRED ${MODULES}) + +# external modules like OpenMP: +include(FindOpenMP) +if(OPENMP_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") + add_definitions(-DOMP_FOUND) +endif() +###### additional flags ###### +#list(APPEND ${PROJ}_LIBRARIES "-lfftw3_threads") + +# gettext files +set(PO_FILE ${LCPATH}/messages.po) +set(MO_FILE ${LCPATH}/LC_MESSAGES/${PROJ}.mo) +set(RU_FILE ${LCPATH}/ru.po) + +# exe file +#add_executable(${PROJ} ${SOURCES}) +# library +add_library(${PROJ} SHARED ${SOURCES}) +# library header files +set(LIBHEADER "usefull_macros.h") +# -I +include_directories(${${PROJ}_INCLUDE_DIRS}) +# -L +link_directories(${${PROJ}_LIBRARY_DIRS}) +# -D +add_definitions(-DLOCALEDIR=\"${LOCALEDIR}\" + -DPACKAGE_VERSION=\"${PROJ_VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\" + -DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\" + -DMAJOR_VERSION=\"${MAJOR_VESION}\") + +# -l +target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES}) + +set(PCFILE "${CMAKE_BINARY_DIR}/${PROJ}.pc") +configure_file("${PROJ}.pc.in" ${PCFILE} @ONLY) + +set_target_properties(${PROJ} PROPERTIES VERSION ${PROJ_VERSION}) +set_target_properties(${PROJ} PROPERTIES PUBLIC_HEADER ${LIBHEADER}) + +# Installation of the program +include(GNUInstallDirs) +#install(TARGETS ${PROJ} DESTINATION "bin") +install(TARGETS ${PROJ} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(FILES ${PCFILE} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) + +# EXAMPLES +if(DEFINED EXAMPLES AND EXAMPLES EQUAL 1) + add_subdirectory(examples) +endif() + +###### gettext ###### +if(NOT DEFINED NOGETTEXT) + add_definitions(-DGETTEXT) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + message("Generate locale files @ make") + find_package(Gettext REQUIRED) + find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext) + if(NOT GETTEXT_XGETTEXT_EXECUTABLE OR NOT GETTEXT_MSGFMT_EXECUTABLE) + message(FATAL_ERROR "xgettext not found") + endif() + file(MAKE_DIRECTORY ${LCPATH}) + file(MAKE_DIRECTORY ${LCPATH}/LC_MESSAGES) + + add_custom_command( + OUTPUT ${PO_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} --from-code=utf-8 ${SOURCES} -c -k_ -kN_ -o ${PO_FILE} + COMMAND sed -i 's/charset=.*\\\\n/charset=koi8-r\\\\n/' ${PO_FILE} + COMMAND enconv ${PO_FILE} + DEPENDS ${SOURCES} + ) + # we need this to prevent ru.po & .mo from deleting by make clean + add_custom_target( + RU_FILE + COMMAND [ -f ${RU_FILE} ] && ${GETTEXT_MSGMERGE_EXECUTABLE} -Uis ${RU_FILE} ${PO_FILE} || cp ${PO_FILE} ${RU_FILE} + DEPENDS ${PO_FILE} ${SOURCES} + ) + add_custom_target( + MO_FILE + COMMAND make RU_FILE && ${GETTEXT_MSGFMT_EXECUTABLE} ${RU_FILE} -o ${MO_FILE} + DEPENDS ${RU_FILE} + ) + add_dependencies(${PROJ} MO_FILE) + else() # install .mo file + install(FILES ${MO_FILE} DESTINATION "${LOCALEDIR}/ru/LC_MESSAGES") + endif() +endif(NOT DEFINED NOGETTEXT) diff --git a/daemon.c b/daemon.c new file mode 100644 index 0000000..57b5139 --- /dev/null +++ b/daemon.c @@ -0,0 +1,147 @@ +/* + * daemon.c - functions for running in background like a daemon + * + * Copyright 2013 Edward V. Emelianoff + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include // printf, fopen, ... +#include // getpid +#include // perror +#include // opendir +#include // opendir +#include // stat +#include // fcntl +#include // exit +#include // memset +#include "usefull_macros.h" + +/** + * @brief readPSname - read process name from /proc/PID/cmdline + * @param pid - PID of interesting process + * @return filename or NULL if not found + * don't use this function twice for different names without copying + * its returning by strdup, because `name` contains in static array + */ +char *readPSname(pid_t pid){ + static char name[PATH_MAX]; + char *pp = name, byte, path[PATH_MAX]; + FILE *file; + int cntr = 0; + size_t sz; + snprintf(path, PATH_MAX, PROC_BASE "/%d/cmdline", pid); + file = fopen(path, "r"); + if(!file) return NULL; // there's no such file + do{ // read basename + sz = fread(&byte, 1, 1, file); + if(sz != 1) break; + if(byte != '/') *pp++ = byte; + else{ + pp = name; + cntr = 0; + } + }while(byte && cntr++ < PATH_MAX-1); + name[cntr] = 0; + fclose(file); + return name; +} + +/** + * @brief iffound_default - default action when running process found + * @param pid - another process' pid + * Redefine this function for user action + */ +void WEAK iffound_default(pid_t pid){ + /// \nОбнаружен одноименный процесс (pid=%d), выход.\n + fprintf(stderr, _("\nFound running process (pid=%d), exit.\n"), pid); + exit(-1); +} + +/** + * check wether there is a same running process + * exit if there is a running process or error + * Checking have 3 steps: + * 1) lock executable file + * 2) check pidfile (if you run a copy?) + * 3) check /proc for executables with the same name (no/wrong pidfile) + * @param selfname - argv[0] or NULL for non-locking + * @param pidfilename - name of pidfile or NULL if none + */ +void check4running(char *selfname, char *pidfilename){ + DIR *dir; + FILE *pidfile, *fself; + struct dirent *de; + struct stat s_buf; + pid_t pid = 0, self; + struct flock fl; + char *name, *myname; + if(selfname){ // block self + fself = fopen(selfname, "r"); // open self binary to lock + if(!fself){ + WARN("fopen"); + goto selfpid; + } + memset(&fl, 0, sizeof(struct flock)); + fl.l_type = F_WRLCK; + if(fcntl(fileno(fself), F_GETLK, &fl) == -1){ // check locking + WARN("fcntl"); + goto selfpid; + } + if(fl.l_type != F_UNLCK){ // file is locking - exit + iffound_default(fl.l_pid); + } + fl.l_type = F_RDLCK; + if(fcntl(fileno(fself), F_SETLKW, &fl) == -1){ + WARN("fcntl"); + } + } + selfpid: + self = getpid(); // get self PID + if(!(dir = opendir(PROC_BASE))){ // open /proc directory + ERR(PROC_BASE); + } + if(!(name = readPSname(self))){ // error reading self name + ERR("Can't read self name"); + } + myname = strdup(name); + if(pidfilename && stat(pidfilename, &s_buf) == 0){ // pidfile exists + pidfile = fopen(pidfilename, "r"); + if(pidfile){ + if(fscanf(pidfile, "%d", &pid) > 0){ // read PID of (possibly) running process + if((name = readPSname(pid)) && strncmp(name, myname, 255) == 0) + iffound_default(pid); + } + fclose(pidfile); + } + } + // There is no pidfile or it consists a wrong record + while((de = readdir(dir))){ // scan /proc + if(!(pid = (pid_t)atoi(de->d_name)) || pid == self) // pass non-PID files and self + continue; + if((name = readPSname(pid)) && strncmp(name, myname, 255) == 0) + iffound_default(pid); + } + closedir(dir); + free(myname); + if(pidfilename){ + pidfile = fopen(pidfilename, "w"); + /// Не могу открыть PID файл + if(!pidfile) ERR(_("Can't open PID file")); + fprintf(pidfile, "%d\n", self); // write self PID to pidfile + fclose(pidfile); + } +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..5c79772 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.9) +project(examples) + +# common includes & library +include_directories(../) +link_libraries(usefull_macros) + +# exe list +add_executable(helloworld helloworld.c) +add_executable(options options.c cmdlnopts.c) diff --git a/examples/cmdlnopts.c b/examples/cmdlnopts.c new file mode 100644 index 0000000..73851d2 --- /dev/null +++ b/examples/cmdlnopts.c @@ -0,0 +1,87 @@ +/* geany_encoding=koi8-r + * cmdlnopts.c - the only function that parse cmdln args and returns glob parameters + * + * Copyright 2013 Edward V. Emelianoff + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#include +#include +#include +#include +#include +#include "cmdlnopts.h" +#include "usefull_macros.h" + +/* + * here are global parameters initialisation + */ +int help; +glob_pars G; + +#define DEFAULT_COMDEV "/dev/ttyUSB0" +// default PID filename: +#define DEFAULT_PIDFILE "/tmp/testcmdlnopts.pid" + +// DEFAULTS +// default global parameters +glob_pars const Gdefault = { + .device = DEFAULT_COMDEV, + .pidfile = DEFAULT_PIDFILE, + .logfile = NULL // don't save logs +}; + +/* + * Define command line options by filling structure: + * name has_arg flag val type argptr help +*/ +myoption cmdlnopts[] = { +// common options + {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, + {"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.device), _("serial device name (default: " DEFAULT_COMDEV ")")}, + {"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("file to save logs")}, + {"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")}, + end_option +}; + +/** + * Parse command line options and return dynamically allocated structure + * to global parameters + * @param argc - copy of argc from main + * @param argv - copy of argv from main + * @return allocated structure with global parameters + */ +glob_pars *parse_args(int argc, char **argv){ + int i; + void *ptr; + ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr); + size_t hlen = 1024; + char helpstring[1024], *hptr = helpstring; + snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n"); + // format of help: "Usage: progname [args]\n" + change_helpstring(helpstring); + // parse arguments + parseargs(&argc, &argv, cmdlnopts); + if(help) showhelp(-1, cmdlnopts); + if(argc > 0){ + G.rest_pars_num = argc; + G.rest_pars = MALLOC(char *, argc); + for (i = 0; i < argc; i++) + G.rest_pars[i] = strdup(argv[i]); + } + return &G; +} + diff --git a/examples/cmdlnopts.h b/examples/cmdlnopts.h new file mode 100644 index 0000000..c337f3d --- /dev/null +++ b/examples/cmdlnopts.h @@ -0,0 +1,39 @@ +/* geany_encoding=koi8-r + * cmdlnopts.h - comand line options for parceargs + * + * Copyright 2013 Edward V. Emelianoff + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#pragma once +#ifndef __CMDLNOPTS_H__ +#define __CMDLNOPTS_H__ + +/* + * here are some typedef's for global data + */ +typedef struct{ + char *device; // serial device name + char *pidfile; // name of PID file + char *logfile; // logging to this file + int rest_pars_num; // number of rest parameters + char** rest_pars; // the rest parameters: array of char* +} glob_pars; + + +glob_pars *parse_args(int argc, char **argv); +#endif // __CMDLNOPTS_H__ diff --git a/examples/helloworld.c b/examples/helloworld.c new file mode 100644 index 0000000..0a89ef4 --- /dev/null +++ b/examples/helloworld.c @@ -0,0 +1,35 @@ +/* + * This file is part of the usefull_macros project. + * Copyright 2018 Edward V. Emelianoff . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +/** + * This is a simplest example of library usage: "blind" getchar + */ + +int main(){ + initial_setup(); + // setup non-echo non-canonical mode + setup_con(); + green("Press any key...\n"); + char k = mygetchar(); + red("You press %c\n", k); + // don't forget to restore @exit! + restore_console(); + return 0; +} diff --git a/examples/options.c b/examples/options.c new file mode 100644 index 0000000..b3cddc3 --- /dev/null +++ b/examples/options.c @@ -0,0 +1,81 @@ +/* + * This file is part of the usefull_macros project. + * Copyright 2018 Edward V. Emelianoff . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include // signal +#include // exit, free +#include // printf +#include // strdup +#include // sleep +#include "cmdlnopts.h" + +/** + * This is an example of usage: + * - command line arguments, + * - log file, + * - check of another file version running, + * - signals management, + * - serial port reading/writing. + * The `cmdlnopts.[hc]` are intrinsic files of this demo. + */ + +/** + * We REDEFINE the default WEAK function of signal processing + */ +void signals(int sig){ + signal(sig, SIG_IGN); + restore_console(); + restore_tty(); + DBG("Get signal %d, quit.\n", sig); + putlog("Exit with status %d", sig); + exit(sig); +} + +void iffound_default(pid_t pid){ + ERRX("Another copy of this process found, pid=%d. Exit.", pid); +} + +int main(int argc, char *argv[]){ + initial_setup(); + char *self = strdup(argv[0]); + glob_pars *GP = parse_args(argc, argv); + if(GP->rest_pars_num){ + printf("%d extra options:\n", GP->rest_pars_num); + for(int i = 0; i < GP->rest_pars_num; ++i) + printf("%s\n", GP->rest_pars[i]); + } + check4running(self, GP->pidfile); + free(self); + signal(SIGTERM, signals); // kill (-15) - quit + signal(SIGHUP, SIG_IGN); // hup - ignore + signal(SIGINT, signals); // ctrl+C - quit + signal(SIGQUIT, signals); // ctrl+\ - quit + signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z + if(GP->logfile) openlogfile(GP->logfile); + setup_con(); + putlog(("Start application...")); + ; // main stuff goes here + green("Now I will sleep for 10 seconds. Do whatever you want.\n"); + sleep(10); + ; // clean everything + if(GP->pidfile) // remove unnesessary PID file + unlink(GP->pidfile); + restore_console(); + restore_tty(); + return 0; +} diff --git a/locale/ru/LC_MESSAGES/usefull_macros.mo b/locale/ru/LC_MESSAGES/usefull_macros.mo new file mode 100644 index 0000000000000000000000000000000000000000..c2ccd6fef28248155041e670e931d5623ad3414f GIT binary patch literal 1926 zcmZvb&u<$=6vr1RzYG*8zgkqpbCDIQu)zsJM5aYGiQ5`%CrZ*HxS++JY!~dE)$Y2q z5)v1TQ_(UHnw-2wOuEnL_%BJ;;Nw_-4xRwN0-piDkNG2b z4D0onJF)#Q@Cn$Z2Zs5N#(V{Q5$0J?>{|wfj#V%Pegi%Y{sf9T>tGfdcfmB)Cm$T@ zD}pa$&A>_UYmmSnz@uOYiu3*figWz~igO)*Xvj1u`f?Ez=VCFx1V!HOz{kL!LD54O z6n(k{PJws9(;z)8NlEZMP~@qCkUJh?Zvyj3Y{9nZd_tobBIjs4hdjpBZ!#CcxVD`eJnUDH)EOkQS|MXwrKqrtWL_DP|TID90OsOF5pl2~Zf3Q>(UFGV3Y*C%mr~(S!TNI|c zL1nI6d@S6u6&tmsylyjkg?tY{T8KXGH=dIrr$`X5hyRQtC~n2 z$9@w!DINT2;m7W`@pRDio-cE#~Mx#m6>u*FLUwPWoY&j%}z*(+iSiP_HVjA z1r0y&+OF^KY*Hr-esR0q15akK^J~zE-;D1DYgl=0a>9*1w)B$H-!8R-jXv!<&2Xo; zJ$yQTzlUV&&Sq=u@3^5maJxN^IziZ@0o`hLo2?tW-ayn1-J4!}KQ?If_dF!?oo+7- zHk-pu&Nv``T%+1SZ&({&U8A){dp+0pw#LCDuXtsLB#j)cVB>%7kx+bXqcZ(&&+&u7 zAz@?ZP^$1X1HT)zw#S~qPA6#j!ZUh`f4Z%}rGf8KBj_NjhYkkR@SKjb8~BcN`!{c` z=|rs*S$3TU>XO&qqF8Fwi34UK-$_b$!eFg`gYG$(c#MN#hogcJBl?p->Q3kkyw, YEAR. +# +#, fuzzy +msgid "" +msgstr "Project-Id-Version: PACKAGE VERSION\n" + "Report-Msgid-Bugs-To: \n" + "POT-Creation-Date: 2018-12-07 02:52+0300\n" + "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" + "Last-Translator: FULL NAME \n" + "Language-Team: LANGUAGE \n" + "Language: \n" + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=koi8-r\n" + "Content-Transfer-Encoding: 8bit\n" + +#. / \nОбнаружен одноименный процесс (pid=%d), выход.\n +#: /Big/Data/C_sources/snippets_library/daemon.c:70 +#, c-format +msgid "\n" + "Found running process (pid=%d), exit.\n" +msgstr "\n" + "Обнаружен одноименный процесс (pid=%d), выход.\n" + +#. / %s: необходим аргумент! +#: /Big/Data/C_sources/snippets_library/parseargs.c:483 +#, c-format +msgid "%s: argument needed!" +msgstr "%s: необходим аргумент!" + +#. / Не могу закрыть mmap'нутый файл +#: /Big/Data/C_sources/snippets_library/usefull_macros.c:218 +msgid "Can't close mmap'ed file" +msgstr "Не могу закрыть mmap'нутый файл" + +#. / Не могу перевести порт в эксклюзивный режим +#: /Big/Data/C_sources/snippets_library/usefull_macros.c:352 +msgid "Can't do exclusive open" +msgstr "Не могу перевести порт в эксклюзивный режим" + +#. Get settings +#. / Не могу получить настройки порта +#: /Big/Data/C_sources/snippets_library/usefull_macros.c:345 +msgid "Can't get port settings" +msgstr "Не могу получить настройки порта" + +#. / Не могу munmap +#: /Big/Data/C_sources/snippets_library/usefull_macros.c:232 +msgid "Can't munmap" +msgstr "Не могу munmap" + +#. / Не могу открыть %s для чтения +#: /Big/Data/C_sources/snippets_library/usefull_macros.c:201 +#, c-format +msgid "Can't open %s for reading" +msgstr "Не могу открыть %s для чтения" + +#. / Не могу открыть PID файл +#: /Big/Data/C_sources/snippets_library/daemon.c:143 +msgid "Can't open PID file" +msgstr "Не могу открыть PID файл" + +#. / Не могу открыть логфайл +#: /Big/Data/C_sources/snippets_library/usefull_macros.c:462 +msgid "Can't open log file" +msgstr "Не могу открыть логфайл" + +#. / Не могу открыть порт %s +#: /Big/Data/C_sources/snippets_library/usefull_macros.c:340 +#, c-format +msgid "Can't open port %s" +msgstr "Не могу открыть порт %s" + +#. / Не могу установить настройки +#: /Big/Data/C_sources/snippets_library/usefull_macros.c:365 +msgid "Can't set settings" +msgstr "Не могу установить настройки" + +#. / Не могу настроить консоль +#: /Big/Data/C_sources/snippets_library/usefull_macros.c:263 +msgid "Can't setup console" +msgstr "Не могу настроить консоль" + +#. / Не могу выполнить stat %s +#: /Big/Data/C_sources/snippets_library/usefull_macros.c:206 +#, c-format +msgid "Can't stat %s" +msgstr "Не могу выполнить stat %s" + +#. / Целое вне допустимого диапазона +#: /Big/Data/C_sources/snippets_library/parseargs.c:84 +msgid "Integer out of range" +msgstr "Целое вне допустимого диапазона" + +#. / Ошибка mmap +#: /Big/Data/C_sources/snippets_library/usefull_macros.c:213 +msgid "Mmap error for input" +msgstr "Ошибка mmap" + +#. / Не задано имя логфайла +#: /Big/Data/C_sources/snippets_library/usefull_macros.c:454 +msgid "Need filename for log file" +msgstr "Не задано имя логфайла" + +#. / Не задано имя файла! +#: /Big/Data/C_sources/snippets_library/usefull_macros.c:196 +msgid "No filename given!" +msgstr "Не задано имя файла!" + +#. / Пробую открыть логфайл %s в режиме дополнения\n +#: /Big/Data/C_sources/snippets_library/usefull_macros.c:458 +#, c-format +msgid "Try to open log file %s in append mode\n" +msgstr "Пробую открыть логфайл %s в режиме дополнения\n" + +#. / Неправильный аргумент \"%s\" параметра \"%s\" +#: /Big/Data/C_sources/snippets_library/parseargs.c:488 +#, c-format +msgid "Wrong argument \"%s\" of parameter \"%s\"" +msgstr "Неправильный аргумент \"%s\" параметра \"%s\"" + +#. amount of pcount and/or scount wrong +#. / Неправильный формат строки помощи +#: /Big/Data/C_sources/snippets_library/parseargs.c:54 +msgid "Wrong helpstring!" +msgstr "Неправильный формат строки помощи" + +#. / Неправильный параметр: %s +#: /Big/Data/C_sources/snippets_library/parseargs.c:478 +#, c-format +msgid "Wrong parameter: %s" +msgstr "Неправильный параметр: %s" diff --git a/parseargs.c b/parseargs.c new file mode 100644 index 0000000..41aba75 --- /dev/null +++ b/parseargs.c @@ -0,0 +1,495 @@ +/* geany_encoding=koi8-r + * parseargs.c - parsing command line arguments & print help + * + * Copyright 2013 Edward V. Emelianoff + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include // printf +#include // getopt_long +#include // calloc, exit, strtoll +#include // assert +#include // strdup, strchr, strlen +#include // strcasecmp +#include // INT_MAX & so on +#include // gettext +#include // isalpha +#include "usefull_macros.h" + +char *helpstring = "%s\n"; + +/** + * @brief change_helpstring - change standard help header + * @param str (i) - new format (MAY consist ONE "%s" for progname) + */ +void change_helpstring(char *s){ + int pcount = 0, scount = 0; + char *str = s; + // check `helpstring` and set it to default in case of error + for(; pcount < 2; str += 2){ + if(!(str = strchr(str, '%'))) break; + if(str[1] != '%') pcount++; // increment '%' counter if it isn't "%%" + else{ + str += 2; // pass next '%' + continue; + } + if(str[1] == 's') scount++; // increment "%s" counter + }; + if(pcount > 1 || pcount != scount){ // amount of pcount and/or scount wrong + /// Неправильный формат строки помощи + ERRX(_("Wrong helpstring!")); + } + helpstring = s; +} + +/** + * @brief myatoll - carefull atoll/atoi + * @param num (o) - returning value (or NULL if you wish only check number) - allocated by user + * @param str (i) - string with number must not be NULL + * @param t (i) - T_INT for integer or T_LLONG for long long (if argtype would be wided, may add more) + * @return TRUE if conversion done without errors, FALSE otherwise + */ +static bool myatoll(void *num, char *str, argtype t){ + long long tmp, *llptr; + int *iptr; + char *endptr; + assert(str); + assert(num); + tmp = strtoll(str, &endptr, 0); + if(endptr == str || *str == '\0' || *endptr != '\0') + return FALSE; + switch(t){ + case arg_longlong: + llptr = (long long*) num; + *llptr = tmp; + break; + case arg_int: + default: + if(tmp < INT_MIN || tmp > INT_MAX){ + /// Целое вне допустимого диапазона + WARNX(_("Integer out of range")); + return FALSE; + } + iptr = (int*)num; + *iptr = (int)tmp; + } + return TRUE; +} + +// the same as myatoll but for double +// There's no NAN & INF checking here (what if they would be needed?) +static bool myatod(void *num, const char *str, argtype t){ + double tmp, *dptr; + float *fptr; + char *endptr; + assert(str); + tmp = strtod(str, &endptr); + if(endptr == str || *str == '\0' || *endptr != '\0') + return FALSE; + switch(t){ + case arg_double: + dptr = (double *) num; + *dptr = tmp; + break; + case arg_float: + default: + fptr = (float *) num; + *fptr = (float)tmp; + break; + } + return TRUE; +} + +/** + * @brief get_optind - get index of current option in array options + * @param opt (i) - returning val of getopt_long + * @param options (i) - array of options + * @return index in array + */ +static int get_optind(int opt, myoption *options){ + int oind; + myoption *opts = options; + assert(opts); + for(oind = 0; opts->name && opts->val != opt; oind++, opts++); + if(!opts->name || opts->val != opt) // no such parameter + showhelp(-1, options); + return oind; +} + +/** + * @brief get_aptr - reallocate new value in array of multiple repeating arguments + * @arg paptr (io) - address of pointer to array (**void) + * @arg type - its type (for realloc) + * @return pointer to new (next) value + */ +void *get_aptr(void *paptr, argtype type){ + int i = 1; + void **aptr = *((void***)paptr); + if(aptr){ // there's something in array + void **p = aptr; + while(*p++) ++i; + } + size_t sz = 0; + switch(type){ + default: + case arg_none: + /// "Не могу использовать несколько параметров без аргументов!" + ERRX("Can't use multiple args with arg_none!"); + break; + case arg_int: + sz = sizeof(int); + break; + case arg_longlong: + sz = sizeof(long long); + break; + case arg_double: + sz = sizeof(double); + break; + case arg_float: + sz = sizeof(float); + break; + case arg_string: + sz = 0; + break; + /* case arg_function: + sz = sizeof(argfn *); + break;*/ + } + aptr = realloc(aptr, (i + 1) * sizeof(void*)); + *((void***)paptr) = aptr; + aptr[i] = NULL; + if(sz){ + aptr[i - 1] = malloc(sz); + }else + aptr[i - 1] = &aptr[i - 1]; + return aptr[i - 1]; +} + + +/** + * @brief parseargs - parse command line arguments + * ! If arg is string, then value will be strdup'ed! + * + * @param argc (io) - address of argc of main(), return value of argc stay after `getopt` + * @param argv (io) - address of argv of main(), return pointer to argv stay after `getopt` + * BE CAREFUL! if you wanna use full argc & argv, save their original values before + * calling this function + * @param options (i) - array of `myoption` for arguments parcing + * + * @exit: in case of error this function show help & make `exit(-1)` + */ +void parseargs(int *argc, char ***argv, myoption *options){ + char *short_options, *soptr; + struct option *long_options, *loptr; + size_t optsize, i; + myoption *opts = options; + // check whether there is at least one options + assert(opts); + assert(opts[0].name); + // first we count how much values are in opts + for(optsize = 0; opts->name; optsize++, opts++); + // now we can allocate memory + short_options = calloc(optsize * 3 + 1, 1); // multiply by three for '::' in case of args in opts + long_options = calloc(optsize + 1, sizeof(struct option)); + opts = options; loptr = long_options; soptr = short_options; + // in debug mode check the parameters are not repeated +#ifdef EBUG + char **longlist = MALLOC(char*, optsize); + char *shortlist = MALLOC(char, optsize); +#endif + // fill short/long parameters and make a simple checking + for(i = 0; i < optsize; i++, loptr++, opts++){ + // check + assert(opts->name); // check name +#ifdef EBUG + longlist[i] = strdup(opts->name); +#endif + if(opts->has_arg){ + assert(opts->type != arg_none); // check error with arg type + assert(opts->argptr); // check pointer + } + if(opts->type != arg_none) // if there is a flag without arg, check its pointer + assert(opts->argptr); + // fill long_options + // don't do memcmp: what if there would be different alignment? + loptr->name = opts->name; + loptr->has_arg = (opts->has_arg < MULT_PAR) ? opts->has_arg : 1; + loptr->flag = opts->flag; + loptr->val = opts->val; + // fill short options if they are: + if(!opts->flag && opts->val){ +#ifdef EBUG + shortlist[i] = (char) opts->val; +#endif + *soptr++ = opts->val; + if(loptr->has_arg) // add ':' if option has required argument + *soptr++ = ':'; + if(loptr->has_arg == 2) // add '::' if option has optional argument + *soptr++ = ':'; + } + } + // sort all lists & check for repeating +#ifdef EBUG + int cmpstringp(const void *p1, const void *p2){ + return strcmp(* (char * const *) p1, * (char * const *) p2); + } + int cmpcharp(const void *p1, const void *p2){ + return (int)(*(char * const)p1 - *(char *const)p2); + } + qsort(longlist, optsize, sizeof(char *), cmpstringp); + qsort(shortlist,optsize, sizeof(char), cmpcharp); + char *prevl = longlist[0], prevshrt = shortlist[0]; + for(i = 1; i < optsize; ++i){ + if(longlist[i]){ + if(prevl){ + if(strcmp(prevl, longlist[i]) == 0) ERRX("double long arguments: --%s", prevl); + } + prevl = longlist[i]; + } + if(shortlist[i]){ + if(prevshrt){ + if(prevshrt == shortlist[i]) ERRX("double short arguments: -%c", prevshrt); + } + prevshrt = shortlist[i]; + } + } +#endif + // now we have both long_options & short_options and can parse `getopt_long` + while(1){ + int opt; + int oindex = 0, optind = 0; // oindex - number of option in argv, optind - number in options[] + if((opt = getopt_long(*argc, *argv, short_options, long_options, &oindex)) == -1) break; + if(opt == '?'){ + opt = optopt; + optind = get_optind(opt, options); + if(options[optind].has_arg == NEED_ARG || options[optind].has_arg == MULT_PAR) + showhelp(optind, options); // need argument + } + else{ + if(opt == 0 || oindex > 0) optind = oindex; + else optind = get_optind(opt, options); + } + opts = &options[optind]; + // if(opt == 0 && opts->has_arg == NO_ARGS) continue; // only long option changing integer flag + // now check option + if(opts->has_arg == NEED_ARG || opts->has_arg == MULT_PAR) + if(!optarg) showhelp(optind, options); // need argument + void *aptr; + if(opts->has_arg == MULT_PAR){ + aptr = get_aptr(opts->argptr, opts->type); + }else + aptr = opts->argptr; + bool result = TRUE; + // even if there is no argument, but argptr != NULL, think that optarg = "1" + if(!optarg) optarg = "1"; + switch(opts->type){ + default: + case arg_none: + if(opts->argptr) *((int*)aptr) += 1; // increment value + break; + case arg_int: + result = myatoll(aptr, optarg, arg_int); + break; + case arg_longlong: + result = myatoll(aptr, optarg, arg_longlong); + break; + case arg_double: + result = myatod(aptr, optarg, arg_double); + break; + case arg_float: + result = myatod(aptr, optarg, arg_float); + break; + case arg_string: + result = (*((void**)aptr) = (void*)strdup(optarg)); + break; + case arg_function: + result = ((argfn)aptr)(optarg); + break; + } + if(!result){ + showhelp(optind, options); + } + } + *argc -= optind; + *argv += optind; +} + +/** + * @brief argsort - compare function for qsort + * first - sort by short options; second - sort arguments without sort opts (by long options) + */ +static int argsort(const void *a1, const void *a2){ + const myoption *o1 = (myoption*)a1, *o2 = (myoption*)a2; + const char *l1 = o1->name, *l2 = o2->name; + int s1 = o1->val, s2 = o2->val; + int *f1 = o1->flag, *f2 = o2->flag; + // check if both options has short arg + if(f1 == NULL && f2 == NULL && s1 && s2){ // both have short arg + return (s1 - s2); + }else if((f1 != NULL || !s1) && (f2 != NULL || !s2)){ // both don't have short arg - sort by long + return strcmp(l1, l2); + }else{ // only one have short arg -- return it + if(f2 || !s2) return -1; // a1 have short - it is 'lesser' + else return 1; + } +} + +/** + * @brief showhelp - show help information based on myoption->help values + * @param oindex (i) - if non-negative, show only help by myoption[oindex].help + * @param options (i) - array of `myoption` + * + * @exit: run `exit(-1)` !!! + */ +void showhelp(int oindex, myoption *options){ + int max_opt_len = 0; // max len of options substring - for right indentation + const int bufsz = 255; + char buf[bufsz+1]; + myoption *opts = options; + assert(opts); + assert(opts[0].name); // check whether there is at least one options + if(oindex > -1){ // print only one message + opts = &options[oindex]; + printf(" "); + if(!opts->flag && isalpha(opts->val)) printf("-%c, ", opts->val); + printf("--%s", opts->name); + if(opts->has_arg == 1) printf("=arg"); + else if(opts->has_arg == 2) printf("[=arg]"); + printf(" %s\n", _(opts->help)); + exit(-1); + } + // header, by default is just "progname\n" + printf("\n"); + if(strstr(helpstring, "%s")) // print progname + printf(helpstring, __progname); + else // only text + printf("%s", helpstring); + printf("\n"); + // count max_opt_len + do{ + int L = strlen(opts->name); + if(max_opt_len < L) max_opt_len = L; + }while((++opts)->name); + max_opt_len += 14; // format: '-S , --long[=arg]' - get addition 13 symbols + opts = options; + // count amount of options + int N; for(N = 0; opts->name; ++N, ++opts); + if(N == 0) exit(-2); + // Now print all help (sorted) + opts = options; + qsort(opts, N, sizeof(myoption), argsort); + do{ + int p = sprintf(buf, " "); // a little indent + if(!opts->flag && opts->val) // .val is short argument + p += snprintf(buf+p, bufsz-p, "-%c, ", opts->val); + p += snprintf(buf+p, bufsz-p, "--%s", opts->name); + if(opts->has_arg == 1) // required argument + p += snprintf(buf+p, bufsz-p, "=arg"); + else if(opts->has_arg == 2) // optional argument + p += snprintf(buf+p, bufsz-p, "[=arg]"); + assert(p < max_opt_len); // there would be magic if p >= max_opt_len + printf("%-*s%s\n", max_opt_len+1, buf, _(opts->help)); // write options & at least 2 spaces after + ++opts; + }while(--N); + printf("\n\n"); + exit(-1); +} + +/** + * @brief get_suboption - get suboptions from parameter string + * @param str - parameter string + * @param opt - pointer to suboptions structure + * @return TRUE if all OK + */ +bool get_suboption(char *str, mysuboption *opt){ + int findsubopt(char *par, mysuboption *so){ + int idx = 0; + if(!par) return -1; + while(so[idx].name){ + if(strcasecmp(par, so[idx].name) == 0) return idx; + ++idx; + } + return -1; // badarg + } + bool opt_setarg(mysuboption *so, int idx, char *val){ + mysuboption *soptr = &so[idx]; + bool result = FALSE; + void *aptr = soptr->argptr; + switch(soptr->type){ + default: + case arg_none: + if(soptr->argptr) *((int*)aptr) += 1; // increment value + result = TRUE; + break; + case arg_int: + result = myatoll(aptr, val, arg_int); + break; + case arg_longlong: + result = myatoll(aptr, val, arg_longlong); + break; + case arg_double: + result = myatod(aptr, val, arg_double); + break; + case arg_float: + result = myatod(aptr, val, arg_float); + break; + case arg_string: + result = (*((void**)aptr) = (void*)strdup(val)); + break; + case arg_function: + result = ((argfn)aptr)(val); + break; + } + return result; + } + char *tok; + bool ret = FALSE; + char *tmpbuf; + tok = strtok_r(str, ":,", &tmpbuf); + do{ + char *val = strchr(tok, '='); + int noarg = 0; + if(val == NULL){ // no args + val = "1"; + noarg = 1; + }else{ + *val++ = '\0'; + if(!*val || *val == ':' || *val == ','){ // no argument - delimeter after = + val = "1"; noarg = 1; + } + } + int idx = findsubopt(tok, opt); + if(idx < 0){ + /// Неправильный параметр: %s + WARNX(_("Wrong parameter: %s"), tok); + goto returning; + } + if(noarg && opt[idx].has_arg == NEED_ARG){ + /// %s: необходим аргумент! + WARNX(_("%s: argument needed!"), tok); + goto returning; + } + if(!opt_setarg(opt, idx, val)){ + /// Неправильный аргумент \"%s\" параметра \"%s\" + WARNX(_("Wrong argument \"%s\" of parameter \"%s\""), val, tok); + goto returning; + } + }while((tok = strtok_r(NULL, ":,", &tmpbuf))); + ret = TRUE; +returning: + return ret; +} diff --git a/usefull_macros.c b/usefull_macros.c new file mode 100644 index 0000000..ad29770 --- /dev/null +++ b/usefull_macros.c @@ -0,0 +1,492 @@ +/* geany_encoding=koi8-r + * usefull_macros.h - a set of usefull functions: memory, color etc + * + * Copyright 2013 Edward V. Emelianoff + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // PATH_MAX + +#include "usefull_macros.h" + +/** + * @brief signals - signal handler + * @param sig - signal + * default signal handler simply exits with `sig` status + */ +void __attribute__ ((weak)) signals(int sig){ + exit(sig); +} + +/** + * @brief dtime - function for different purposes that need to know time intervals + * @return double value: UNIX time in seconds + */ +double dtime(){ + double t; + struct timeval tv; + gettimeofday(&tv, NULL); + t = tv.tv_sec + ((double)tv.tv_usec)/1e6; + return t; +} + +/******************************************************************************\ + * Coloured terminal +\******************************************************************************/ +int globErr = 0; // errno for WARN/ERR + +// pointers to coloured output printf +int (*red)(const char *fmt, ...); +int (*green)(const char *fmt, ...); +int (*_WARN)(const char *fmt, ...); + +/** + * @brief r_pr_, g_pr_ - format red / green messages + * @param fmt ... - printf-like format + * @return number of printed symbols + */ +int r_pr_(const char *fmt, ...){ + va_list ar; int i; + printf(COLOR_RED); + va_start(ar, fmt); + i = vprintf(fmt, ar); + va_end(ar); + printf(COLOR_OLD); + return i; +} +int g_pr_(const char *fmt, ...){ + va_list ar; int i; + printf(COLOR_GREEN); + va_start(ar, fmt); + i = vprintf(fmt, ar); + va_end(ar); + printf(COLOR_OLD); + return i; +} + +/** + * @brief r_WARN - print red error/warning messages (if output is a tty) + * @param fmt ... - printf-like format + * @return number of printed symbols + */ +int r_WARN(const char *fmt, ...){ + va_list ar; int i = 1; + fprintf(stderr, COLOR_RED); + va_start(ar, fmt); + if(globErr){ + errno = globErr; + vwarn(fmt, ar); + errno = 0; + }else + i = vfprintf(stderr, fmt, ar); + va_end(ar); + i++; + fprintf(stderr, COLOR_OLD "\n"); + return i; +} + +static const char stars[] = "****************************************"; +/** + * @brief s_WARN, r_pr_notty - notty variants of coloured printf + * @param fmt ... - printf-like format + * @return number of printed symbols + */ +int s_WARN(const char *fmt, ...){ + va_list ar; int i; + i = fprintf(stderr, "\n%s\n", stars); + va_start(ar, fmt); + if(globErr){ + errno = globErr; + vwarn(fmt, ar); + errno = 0; + }else + i = +vfprintf(stderr, fmt, ar); + va_end(ar); + i += fprintf(stderr, "\n%s\n", stars); + i += fprintf(stderr, "\n"); + return i; +} +int r_pr_notty(const char *fmt, ...){ + va_list ar; int i; + i = printf("\n%s\n", stars); + va_start(ar, fmt); + i += vprintf(fmt, ar); + va_end(ar); + i += printf("\n%s\n", stars); + return i; +} + +/** + * @brief initial_setup - setup locale & console + * Run this function in the beginning of main() to setup locale & coloured output + */ +void initial_setup(){ + // setup coloured output + if(isatty(STDOUT_FILENO)){ // make color output in tty + red = r_pr_; green = g_pr_; + }else{ // no colors in case of pipe + red = r_pr_notty; green = printf; + } + if(isatty(STDERR_FILENO)) _WARN = r_WARN; + else _WARN = s_WARN; + // Setup locale + setlocale(LC_ALL, ""); + setlocale(LC_NUMERIC, "C"); +#if defined GETTEXT_PACKAGE && defined LOCALEDIR + bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); + textdomain(GETTEXT_PACKAGE); +#endif +} + +/******************************************************************************\ + * Memory +\******************************************************************************/ +/** + * @brief my_alloc - safe memory allocation for macro ALLOC + * @param N - number of elements to allocate + * @param S - size of single element (typically sizeof) + * @return pointer to allocated memory area + */ +void *my_alloc(size_t N, size_t S){ + void *p = calloc(N, S); + if(!p) ERR("malloc"); + //assert(p); + return p; +} + +/** + * @brief My_mmap - mmap file to a memory area + * @param filename (i) - name of file to mmap + * @return stuct with mmap'ed file or die + */ +mmapbuf *My_mmap(char *filename){ + int fd; + char *ptr; + size_t Mlen; + struct stat statbuf; + if(!filename){ + /// Не задано имя файла! + WARNX(_("No filename given!")); + return NULL; + } + if((fd = open(filename, O_RDONLY)) < 0){ + /// Не могу открыть %s для чтения + WARN(_("Can't open %s for reading"), filename); + return NULL; + } + if(fstat (fd, &statbuf) < 0){ + /// Не могу выполнить stat %s + WARN(_("Can't stat %s"), filename); + close(fd); + return NULL; + } + Mlen = statbuf.st_size; + if((ptr = mmap (0, Mlen, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED){ + /// Ошибка mmap + WARN(_("Mmap error for input")); + close(fd); + return NULL; + } + /// Не могу закрыть mmap'нутый файл + if(close(fd)) WARN(_("Can't close mmap'ed file")); + mmapbuf *ret = MALLOC(mmapbuf, 1); + ret->data = ptr; + ret->len = Mlen; + return ret; +} + +/** + * @brief My_munmap - unmap memory file + * @param b (i) - mmap'ed buffer + */ +void My_munmap(mmapbuf *b){ + if(munmap(b->data, b->len)){ + /// Не могу munmap + ERR(_("Can't munmap")); + } + FREE(b); +} + + +/******************************************************************************\ + * Terminal in no-echo mode + * BE CAREFULL! These functions aren't thread-safe! +\******************************************************************************/ +static struct termios oldt, newt; // console flags +static int console_changed = 0; +/** + * @brief restore_console - restore console to default mode + */ +void restore_console(){ + if(console_changed) + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state + console_changed = 0; +} + +/** + * @brief setup_con - setup console to non-canonical noecho mode + */ +void setup_con(){ + if(console_changed) return; + tcgetattr(STDIN_FILENO, &oldt); + newt = oldt; + newt.c_lflag &= ~(ICANON | ECHO); + if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0){ + /// Не могу настроить консоль + WARN(_("Can't setup console")); + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + signals(1); //quit? + } + console_changed = 1; +} + +/** + * @brief read_console - read character from console without echo + * @return char read or zero + */ +int read_console(){ + int rb; + struct timeval tv; + int retval; + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(STDIN_FILENO, &rfds); + tv.tv_sec = 0; tv.tv_usec = 10000; + retval = select(1, &rfds, NULL, NULL, &tv); + if(!retval) rb = 0; + else { + if(FD_ISSET(STDIN_FILENO, &rfds)) rb = getchar(); + else rb = 0; + } + return rb; +} + +/** + * @brief my - getchar() without echo + * wait until at least one character pressed + * @return character read + */ +int mygetchar(){ + int ret; + do ret = read_console(); + while(ret == 0); + return ret; +} + + +/******************************************************************************\ + * TTY with select() + * BE CAREFULL! These functions aren't thread-safe! + * BE CAREFULL! These functions are for one serial port only! +\******************************************************************************/ +static struct termios oldtty, tty; // TTY flags +static int comfd = -1; // TTY fd + +/** + * @brief restore_tty - restore port settings to previous + */ +void restore_tty(){ + if(comfd == -1) return; + if(oldtty.c_cflag) + tcsetattr(comfd, TCSANOW, &oldtty); // return TTY to previous state + close(comfd); + comfd = -1; +} + +/** + * @brief tty_init - init port with 8N1 in non-blocking RW mode + * @param comdev (i) - port device + * @param speed - communication speed + */ +void tty_init(char *comdev, tcflag_t speed){ + if(comfd == -1){ // not opened + if(!comdev){ + WARNX("comdev == NULL"); + signals(11); + } + DBG("Open port..."); + do{ + comfd = open(comdev, O_RDWR|O_NOCTTY|O_NONBLOCK); + }while (comfd == -1 && errno == EINTR); + if(comfd < 0){ + /// Не могу открыть порт %s + WARN(_("Can't open port %s"),comdev); + signals(2); + } + if(tcgetattr(comfd, &oldtty)){ // Get settings + /// Не могу получить настройки порта + WARN(_("Can't get port settings")); + oldtty.c_cflag = 0; + } + DBG("Make exclusive"); + // make exclusive open + if(ioctl(comfd, TIOCEXCL)){ + /// Не могу перевести порт в эксклюзивный режим + WARN(_("Can't do exclusive open")); + close(comfd); + signals(2); + } + } + tty = oldtty; + tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG) + tty.c_oflag = 0; + tty.c_cflag = speed|CS8|CREAD|CLOCAL; // 9.6k, 8N1, RW, ignore line ctrl + tty.c_cc[VMIN] = 0; // non-canonical mode + tty.c_cc[VTIME] = 5; + if(ioctl(comfd, TCSETA, &tty) < 0){ + /// Не могу установить настройки + WARN(_("Can't set settings")); + signals(0); + } + DBG("OK"); +} + +/** + * @brief read_tty - read data from TTY with 10ms timeout + * @param buff (o) - buffer for data read + * @param length - buffer len + * @return amount of bytes read + */ +size_t read_tty(char *buff, size_t length){ + if(comfd < 0) return 0; + ssize_t L = 0, l; + char *ptr = buff; + fd_set rfds; + struct timeval tv; + int retval; + do{ + l = 0; + FD_ZERO(&rfds); + FD_SET(comfd, &rfds); + // wait for 10ms + tv.tv_sec = 0; tv.tv_usec = 10000; + retval = select(comfd + 1, &rfds, NULL, NULL, &tv); + if (!retval) break; + if(FD_ISSET(comfd, &rfds)){ + if((l = read(comfd, ptr, length)) < 1){ + return 0; + } + ptr += l; L += l; + length -= l; + } + }while(l); + return (size_t)L; +} + +/** + * @brief write_tty - write data to serial port + * @param buff (i) - data to write + * @param length - its length + * @return 0 if all OK + */ +int write_tty(const char *buff, size_t length){ + ssize_t L = write(comfd, buff, length); + if((size_t)L != length){ + /// "Ошибка записи!" + WARN("Write error"); + return 1; + } + return 0; +} + +/** + * @brief str2double - safely convert data from string to double + * @param num (o) - double number read from string + * @param str (i) - input string + * @return 1 if success, 0 if fails + */ +int str2double(double *num, const char *str){ + double res; + char *endptr; + if(!str) return 0; + res = strtod(str, &endptr); + if(endptr == str || *str == '\0' || *endptr != '\0'){ + /// "Неправильный формат числа double!" + WARNX("Wrong double number format!"); + return FALSE; + } + if(num) *num = res; // you may run it like myatod(NULL, str) to test wether str is double number + return TRUE; +} + +/******************************************************************************\ + * Logging to file + * BE CAREFULL!!! There's only one log file per process! +\******************************************************************************/ +FILE *Flog = NULL; // log file descriptor +char *logname = NULL; +time_t log_open_time = 0; +/** + * @brief openlogfile - try to open log file + * @param name (i) - log file name + * if failed show warning message + */ +void openlogfile(char *name){ + if(!name){ + /// Не задано имя логфайла + WARNX(_("Need filename for log file")); + return; + } + /// Пробую открыть логфайл %s в режиме дополнения\n + green(_("Try to open log file %s in append mode\n"), name); + fflush(stdout); + if(!(Flog = fopen(name, "a"))){ + /// Не могу открыть логфайл + WARN(_("Can't open log file")); + return; + } + log_open_time = time(NULL); + logname = name; +} + +/** + * Save message to log file, rotate logs every 24 hours + */ +int putlog(const char *fmt, ...){ + if(!Flog) return 0; + time_t t_now = time(NULL); + if(t_now - log_open_time > 86400){ // rotate log + fprintf(Flog, "\n\t\t%sRotate log\n", ctime(&t_now)); + fclose(Flog); + char newname[PATH_MAX]; + snprintf(newname, PATH_MAX, "%s.old", logname); + if(rename(logname, newname)) WARN("rename()"); + openlogfile(logname); + if(!Flog) return 0; + } + int i = fprintf(Flog, "\n\t\t%s", ctime(&t_now)); + va_list ar; + va_start(ar, fmt); + i = vfprintf(Flog, fmt, ar); + va_end(ar); + fprintf(Flog, "\n"); + fflush(Flog); + return i; +} diff --git a/usefull_macros.h b/usefull_macros.h new file mode 100644 index 0000000..0716b36 --- /dev/null +++ b/usefull_macros.h @@ -0,0 +1,254 @@ +/* geany_encoding=koi8-r + * usefull_macros.h - a set of usefull macros: memory, color etc + * + * Copyright 2013 Edward V. Emelianoff + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#pragma once +#ifndef __USEFULL_MACROS_H__ +#define __USEFULL_MACROS_H__ + +#include // tcflag_t +#include // bool +#include // pid_t +#include // errno + +#if defined GETTEXT +/* + * GETTEXT + */ +#include +#define _(String) gettext(String) +#define gettext_noop(String) String +#define N_(String) gettext_noop(String) +#else +#define _(String) (String) +#define N_(String) (String) +#endif + +/******************************************************************************\ + The original usefull_macros.h +\******************************************************************************/ + +// unused arguments of functions +#define _U_ __attribute__((__unused__)) +// break absent in `case` +#define FALLTHRU __attribute__ ((fallthrough)) +// and synonym for FALLTHRU +#define NOBREAKHERE __attribute__ ((fallthrough)) +// weak functions +#define WEAK __attribute__ ((weak)) + +/* + * Coloured messages output + */ +#define COLOR_RED "\033[1;31;40m" +#define COLOR_GREEN "\033[1;32;40m" +#define COLOR_OLD "\033[0;0;0m" + +#ifndef FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (1) +#endif + +/* + * ERROR/WARNING messages + */ +// global error +extern int globErr; +void WEAK signals(int sig); +#define ERR(...) do{globErr=errno; _WARN(__VA_ARGS__); signals(9);}while(0) +#define ERRX(...) do{globErr=0; _WARN(__VA_ARGS__); signals(9);}while(0) +#define WARN(...) do{globErr=errno; _WARN(__VA_ARGS__);}while(0) +#define WARNX(...) do{globErr=0; _WARN(__VA_ARGS__);}while(0) + +/* + * print function name, debug messages + * debug mode, -DEBUG + */ +#ifdef EBUG + #define FNAME() do{ fprintf(stderr, COLOR_OLD); \ + fprintf(stderr, "\n%s (%s, line %d)\n", __func__, __FILE__, __LINE__);} while(0) + #define DBG(...) do{ fprintf(stderr, COLOR_OLD); \ + fprintf(stderr, "%s (%s, line %d): ", __func__, __FILE__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n");} while(0) +#else + #define FNAME() do{}while(0) + #define DBG(...) do{}while(0) +#endif //EBUG + +/* + * Memory allocation + */ +#define ALLOC(type, var, size) type * var = ((type *)my_alloc(size, sizeof(type))) +#define MALLOC(type, size) ((type *)my_alloc(size, sizeof(type))) +#define FREE(ptr) do{if(ptr){free(ptr); ptr = NULL;}}while(0) + +#ifndef DBL_EPSILON +#define DBL_EPSILON (2.2204460492503131e-16) +#endif + +// double value of UNIX time +double dtime(); + +// functions for color output in tty & no-color in pipes +int (*red)(const char *fmt, ...); +int (*_WARN)(const char *fmt, ...); +int (*green)(const char *fmt, ...); +// safe allocation +void * my_alloc(size_t N, size_t S); +// setup locales & other +void initial_setup(); + +// mmap file +typedef struct{ + char *data; + size_t len; +} mmapbuf; +mmapbuf *My_mmap(char *filename); +void My_munmap(mmapbuf *b); + +// console in non-echo mode +void restore_console(); +void setup_con(); +int read_console(); +int mygetchar(); + +// serial port +void restore_tty(); +void tty_init(char *comdev, tcflag_t speed); +size_t read_tty(char *buff, size_t length); +int write_tty(const char *buff, size_t length); + +// convert string to double with checking +int str2double(double *num, const char *str); + +// logging +void openlogfile(char *name); +int putlog(const char *fmt, ...); + +/******************************************************************************\ + The original parseargs.h +\******************************************************************************/ +// macro for argptr +#define APTR(x) ((void*)x) + +// if argptr is a function: +typedef bool(*argfn)(void *arg); + +/** + * type of getopt's argument + * WARNING! + * My function change value of flags by pointer, so if you want to use another type + * make a latter conversion, example: + * char charg; + * int iarg; + * myoption opts[] = { + * {"value", 1, NULL, 'v', arg_int, &iarg, "char val"}, ..., end_option}; + * ..(parse args).. + * charg = (char) iarg; + */ +typedef enum { + arg_none = 0, // no arg + arg_int, // integer + arg_longlong, // long long + arg_double, // double + arg_float, // float + arg_string, // char * + arg_function // parse_args will run function `bool (*fn)(char *optarg, int N)` +} argtype; + +/** + * Structure for getopt_long & help + * BE CAREFUL: .argptr is pointer to data or pointer to function, + * conversion depends on .type + * + * ATTENTION: string `help` prints through macro PRNT(), bu default it is gettext, + * but you can redefine it before `#include "parseargs.h"` + * + * if arg is string, then value wil be strdup'ed like that: + * char *str; + * myoption opts[] = {{"string", 1, NULL, 's', arg_string, &str, "string val"}, ..., end_option}; + * *(opts[1].str) = strdup(optarg); + * in other cases argptr should be address of some variable (or pointer to allocated memory) + * + * NON-NULL argptr should be written inside macro APTR(argptr) or directly: (void*)argptr + * + * !!!LAST VALUE OF ARRAY SHOULD BE `end_option` or ZEROS !!! + * + */ +typedef enum{ + NO_ARGS = 0, // first three are the same as in getopt_long + NEED_ARG = 1, + OPT_ARG = 2, + MULT_PAR +} hasarg; + +typedef struct{ + // these are from struct option: + const char *name; // long option's name + hasarg has_arg; // 0 - no args, 1 - nesessary arg, 2 - optionally arg, 4 - need arg & key can repeat (args are stored in null-terminated array) + int *flag; // NULL to return val, pointer to int - to set its value of val (function returns 0) + int val; // short opt name (if flag == NULL) or flag's value + // and these are mine: + argtype type; // type of argument + void *argptr; // pointer to variable to assign optarg value or function `bool (*fn)(char *optarg, int N)` + const char *help; // help string which would be shown in function `showhelp` or NULL +} myoption; + +/* + * Suboptions structure, almost the same like myoption + * used in parse_subopts() + */ +typedef struct{ + const char *name; + hasarg has_arg; + argtype type; + void *argptr; +} mysuboption; + +// last string of array (all zeros) +#define end_option {0,0,0,0,0,0,0} +#define end_suboption {0,0,0,0} + +extern const char *__progname; + +void showhelp(int oindex, myoption *options); +void parseargs(int *argc, char ***argv, myoption *options); +void change_helpstring(char *s); +bool get_suboption(char *str, mysuboption *opt); + + +/******************************************************************************\ + The original daemon.h +\******************************************************************************/ +#ifndef PROC_BASE +#define PROC_BASE "/proc" +#endif + +// default function to run if another process found +void WEAK iffound_default(pid_t pid); +// check that our process is exclusive +void check4running(char *selfname, char *pidfilename); +// read name of process by its PID +char *readPSname(pid_t pid); +#endif // __USEFULL_MACROS_H__ diff --git a/usefull_macros.pc.in b/usefull_macros.pc.in new file mode 100644 index 0000000..441b10e --- /dev/null +++ b/usefull_macros.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: @PROJ@ +Description: Library with a lot of usefull snippets +Version: @VERSION@ +Libs: -L${libdir} -l@PROJ@ +Cflags: -I${includedir}