Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • e3/wrappers/e3-require
  • waynelewis/e3-require
  • krisztianloki/e3-require
  • timokorhonen/e3-require
  • juntongliu/e3-require
  • roryclarke/e3-require
  • alfiorizzo/e3-require
  • lucasmagalhaes/e3-require
  • lucas-module-testgroup/e3-require
  • grzegorzkowalski/e3-require
  • anderslindh1/e3-require
11 results
Show changes
Showing
with 3199 additions and 4 deletions
.PHONY: vlibs
#.PHONY: vlibs
vlibs: conf
$(QUIET) $(E3_MODULE_MAKE_CMDS) vlibs
## Install vendor libraries
#vlibs: conf
# $(QUIET) $(E3_MODULE_MAKE_CMDS) vlibs
# Version 3.3.1
## Finished
* Remove `loadIocsh`, which simply duplicates `runScript` for some reason. (Done by Krisztian f02d2c88ffd802f78fa61795486d1bd5ed13e96a)
* Fix the metadata for the wrapper e.g. the url should not fetch origin, but the first remote. FIXED 57dc892bfb29c8ddad82baf2d2f287a766800dca
* Not sure why the description fails for the wrapper, however. Aha: the ifeq in CONFIG_E3_MAKEFILE should compare $$? not $?. FIXED 57dc892bfb29c8ddad82baf2d2f287a766800dca
* `make build` followed by `make cellinstall` should not do a rebuild. This is done because
`INSTBASE` changes, and for `make cellinstall` we should ignore that. FIXED (require/2f2016179004eae6811f6f3a5567a0db1fd8b81e)
* Allow SKIP_TEST FIXED 3590a3a8ced658251a333c59396cdbe836b418a4
* `make patch` does not respect build numbers: FIXED 7388f41d19230141e5c54ed2130109d30e52514c
* Fix the install permissions for cellMods FIXED aa4749f887ed92fb90f577326c9e756fadbc081b
* `cellinstall` should write the version of base/require! b9168c9a3479bb4efe3df1e7bbfdb667903aa5ab
* require gets its version from CONFIG_MODULE, so why is it specified in RELEASE? This is confusing. ac9ec7b6e73dc5968b5500f8daf93155fb4699fe
* Remove `rm -rf cellMods` from `celluninstall` target; this is not a great idea if we are using multiple modules installed in the same place. 598cf611260fb40780db204cdc814f7c3a0e305e
* Go through and figure out if we should remove all of the `sudo` usage. 922905e247d6f6ac309b0909f994576f9fedc2b5
## Not done
* Remove more vxWorks/3.x code in e.g. runScript.c and expr.c
* Also, we need to be able to test once it is installed. This would fit with the iterating thing that I had made.
* The auto-load build number thing doesn't seem to work correctly with build descriptors
* Figure out how to fix the fact that asyn, on build, installs devEpics.dbd *** IMPORTANT <=== Needs a patch on EPICS base
* `make cellinstall` does not install vendor libraries anywhere.
* Moreover, vlibs are installed with no reference to `$(T_A)`, which isn't a good idea! One possible idea:
we could move the `vlibs` target into driver.makefile instead of letting it reside outside in the
module makefile. This needs to be discussed.
* module_DB vs module_DIR: DIR includes trailing slash, DB does not. Fix?
* Should there be an error message if non-existent versions of e.g. sequencer are specified?
* base/require version should be added to shared library object; this is needed for cellinstall but should
more generally be used on startup to check that the library was compiled with the running version of base/require.
\ No newline at end of file
Subproject commit d2a89ffee8d5b40f1b37cf0be378790f72cf71f6
record (waveform, "$(REQUIRE_IOC):MODULES")
{
field (DESC, "List of loaded modules")
field (FTVL, "STRING")
field (NELM, "$(MODULE_COUNT)")
field (PINI, "YES")
}
record (waveform, "$(REQUIRE_IOC):VERSIONS")
{
field (DESC, "Versions of loaded modules")
field (FTVL, "STRING")
field (NELM, "$(MODULE_COUNT)")
field (PINI, "YES")
}
record (waveform, "$(REQUIRE_IOC):MOD_VER")
{
field (DESC, "List of loaded modules")
field (FTVL, "CHAR")
field (NELM, "$(BUFFER_SIZE)")
field (PINI, "YES")
}
record (stringin, "$(REQUIRE_IOC):$(MODULE)_VER")
{
field (DESC, "Module $(MODULE) version")
field (VAL, "$(VERSION)")
field (PINI, "YES")
}
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "asprintf.h"
/* some implementations have __va_copy instead of va_copy */
#if !defined(va_copy) && defined (__va_copy)
#define va_copy __va_copy
#endif
int vasprintf(char** pbuffer, const char* format, va_list ap)
{
int len = -1;
#ifdef va_copy
va_list ap2;
va_copy(ap2, ap);
#else
/* if we have no va_copy, we probably don't need one */
#define ap2 ap
#endif
#if defined(_WIN32)
len = _vscprintf(format, ap2);
#else
len = vsnprintf(NULL, 0, format, ap2);
#endif
#ifdef va_copy
va_end(ap2);
#endif
if (len <= 0)
{
fprintf(stderr, "vasprintf: error calculating needed size\n");
return -1;
}
*pbuffer = malloc(len+1);
if (*pbuffer == NULL) return -1;
return vsprintf(*pbuffer, format, ap);
}
int asprintf(char** pbuffer, const char* format, ...)
{
va_list ap;
int len;
va_start(ap, format);
len = vasprintf(pbuffer, format, ap);
va_end(ap);
return len;
}
#ifndef asprintf_h
#define asprintf_h
#ifdef __cplusplus
extern "C" {
#endif
#include <stdarg.h>
#ifndef __GNUC__
#define __attribute__(arg)
#endif
int asprintf(char** pbuffer, const char* format, ...) __attribute__((__format__(__printf__,2,3)));
int vasprintf(char** pbuffer, const char* format, va_list ap) __attribute__((__format__(__printf__,2,0)));
#ifdef __cplusplus
}
#endif
#endif
registrar(dbLoadTemplateRegister)
/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbLoadTemplate.h */
#ifndef INCdbLoadTemplateh
#define INCdbLoadTemplateh
#include "shareLib.h"
epicsShareFunc int dbLoadTemplate(
const char *sub_file, const char *cmd_collect, const char *path);
#endif /*INCdbLoadTemplateh*/
%{
/*************************************************************************\
* Copyright (c) 2006 UChicago, as Operator of Argonne
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* for vasprintf */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#if !defined (_WIN32)
#include <unistd.h>
#endif
#include "macLib.h"
#include "dbmf.h"
#include "dbAccess.h"
#include "dbLoadTemplate.h"
#include "osiFileName.h"
#include "epicsVersion.h"
#if defined(vxWorks) || defined (_WIN32)
#include "asprintf.h"
#endif
#ifdef BASE_VERSION
#define EPICS_3_13
extern void dbLoadRecords(const char*, const char*);
#else
#include "iocsh.h"
#include "epicsExport.h"
#endif
#if (EPICS_VERSION*10000+EPICS_REVISION*100+EPICS_MODIFICATION<31412)
#define dbmfStrdup(s) strcpy(dbmfMalloc(strlen((char*)(s))+1),(char*)(s))
#endif
#if (EPICS_VERSION*10000+EPICS_REVISION*100+EPICS_MODIFICATION>=31600)
#define dbmfStrdup(s) dbmfStrdup((char*)s)
#endif
static int line_num;
static int yyerror(char* str);
static char *sub_collect = NULL;
static char *sub_locals;
static char **vars = NULL;
static char *db_file_name = NULL;
static int var_count, sub_count;
/* We allocate MAX_VAR_FACTOR chars in the sub_collect string for each
* "variable=value," segment, and will accept at most dbTemplateMaxVars
* template variables. The user can adjust that variable to increase
* the number of variables or the length allocated for the buffer.
*/
#define MAX_VAR_FACTOR 50
int dbTemplateMaxVars = 100;
%}
%start substitution_file
%token <Str> WORD QUOTE
%token DBFILE
%token PATTERN
%token GLOBAL
%token EQUALS COMMA
%left O_PAREN C_PAREN
%left O_BRACE C_BRACE
%union
{
int Int;
char Char;
char *Str;
double Real;
}
%%
substitution_file: global_or_template
| substitution_file global_or_template
;
global_or_template: global_definitions
| template_substitutions
;
global_definitions: GLOBAL O_BRACE C_BRACE
| GLOBAL O_BRACE variable_definitions C_BRACE
{
#ifdef ERROR_STUFF
fprintf(stderr, "global_definitions: %s\n", sub_collect+1);
#endif
sub_locals += strlen(sub_locals);
}
;
template_substitutions: template_filename O_BRACE C_BRACE
{
#ifdef ERROR_STUFF
fprintf(stderr, "template_substitutions: %s unused\n", db_file_name);
#endif
dbmfFree(db_file_name);
db_file_name = NULL;
}
| template_filename O_BRACE substitutions C_BRACE
{
#ifdef ERROR_STUFF
fprintf(stderr, "template_substitutions: %s finished\n", db_file_name);
#endif
dbmfFree(db_file_name);
db_file_name = NULL;
}
;
template_filename: DBFILE WORD
{
#ifdef ERROR_STUFF
fprintf(stderr, "template_filename: %s\n", $2);
#endif
var_count = 0;
db_file_name = dbmfMalloc(strlen($2)+1);
strcpy(db_file_name, $2);
dbmfFree($2);
}
| DBFILE QUOTE
{
#ifdef ERROR_STUFF
fprintf(stderr, "template_filename: \"%s\"\n", $2);
#endif
var_count = 0;
db_file_name = dbmfMalloc(strlen($2)+1);
strcpy(db_file_name, $2);
dbmfFree($2);
}
;
substitutions: pattern_substitutions
| variable_substitutions
;
pattern_substitutions: PATTERN O_BRACE C_BRACE
| PATTERN O_BRACE C_BRACE pattern_definitions
| PATTERN O_BRACE pattern_names C_BRACE
| PATTERN O_BRACE pattern_names C_BRACE pattern_definitions
;
pattern_names: pattern_name
| pattern_names COMMA
| pattern_names pattern_name
;
pattern_name: WORD
{
#ifdef ERROR_STUFF
fprintf(stderr, "pattern_name: [%d] = %s\n", var_count, $1);
#endif
if (var_count >= dbTemplateMaxVars) {
fprintf(stderr,
"More than dbTemplateMaxVars = %d macro variables used\n",
dbTemplateMaxVars);
yyerror(NULL);
}
else {
vars[var_count] = dbmfMalloc(strlen($1)+1);
strcpy(vars[var_count], $1);
var_count++;
dbmfFree($1);
}
}
;
pattern_definitions: pattern_definition
| pattern_definitions pattern_definition
;
pattern_definition: global_definitions
| O_BRACE C_BRACE
{
#ifdef ERROR_STUFF
fprintf(stderr, "pattern_definition: pattern_values empty\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif
dbLoadRecords(db_file_name, sub_collect+1);
}
| O_BRACE pattern_values C_BRACE
{
#ifdef ERROR_STUFF
fprintf(stderr, "pattern_definition:\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif
dbLoadRecords(db_file_name, sub_collect+1);
*sub_locals = '\0';
sub_count = 0;
}
| WORD O_BRACE pattern_values C_BRACE
{ /* DEPRECATED SYNTAX */
fprintf(stderr,
"dbLoadTemplate: Substitution file uses deprecated syntax.\n"
" the string '%s' on line %d that comes just before the\n"
" '{' character is extraneous and should be removed.\n",
$1, line_num);
#ifdef ERROR_STUFF
fprintf(stderr, "pattern_definition:\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif
dbLoadRecords(db_file_name, sub_collect+1);
dbmfFree($1);
*sub_locals = '\0';
sub_count = 0;
}
;
pattern_values: pattern_value
| pattern_values COMMA
| pattern_values pattern_value
;
pattern_value: QUOTE
{
#ifdef ERROR_STUFF
fprintf(stderr, "pattern_value: [%d] = \"%s\"\n", sub_count, $1);
#endif
if (sub_count < var_count) {
strcat(sub_locals, ",");
strcat(sub_locals, vars[sub_count]);
strcat(sub_locals, "=\"");
strcat(sub_locals, $1);
strcat(sub_locals, "\"");
sub_count++;
} else {
fprintf(stderr, "dbLoadTemplate: Too many values given, line %d.\n",
line_num);
}
dbmfFree($1);
}
| WORD
{
#ifdef ERROR_STUFF
fprintf(stderr, "pattern_value: [%d] = %s\n", sub_count, $1);
#endif
if (sub_count < var_count) {
strcat(sub_locals, ",");
strcat(sub_locals, vars[sub_count]);
strcat(sub_locals, "=");
strcat(sub_locals, $1);
sub_count++;
} else {
fprintf(stderr, "dbLoadTemplate: Too many values given, line %d.\n",
line_num);
}
dbmfFree($1);
}
;
variable_substitutions: variable_substitution
| variable_substitutions variable_substitution
;
variable_substitution: global_definitions
| O_BRACE C_BRACE
{
#ifdef ERROR_STUFF
fprintf(stderr, "variable_substitution: variable_definitions empty\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif
dbLoadRecords(db_file_name, sub_collect+1);
}
| O_BRACE variable_definitions C_BRACE
{
#ifdef ERROR_STUFF
fprintf(stderr, "variable_substitution:\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif
dbLoadRecords(db_file_name, sub_collect+1);
*sub_locals = '\0';
}
| WORD O_BRACE variable_definitions C_BRACE
{ /* DEPRECATED SYNTAX */
fprintf(stderr,
"dbLoadTemplate: Substitution file uses deprecated syntax.\n"
" the string '%s' on line %d that comes just before the\n"
" '{' character is extraneous and should be removed.\n",
$1, line_num);
#ifdef ERROR_STUFF
fprintf(stderr, "variable_substitution:\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif
dbLoadRecords(db_file_name, sub_collect+1);
dbmfFree($1);
*sub_locals = '\0';
}
;
variable_definitions: variable_definition
| variable_definitions COMMA
| variable_definitions variable_definition
;
variable_definition: WORD EQUALS WORD
{
#ifdef ERROR_STUFF
fprintf(stderr, "variable_definition: %s = %s\n", $1, $3);
#endif
strcat(sub_locals, ",");
strcat(sub_locals, $1);
strcat(sub_locals, "=");
strcat(sub_locals, $3);
dbmfFree($1); dbmfFree($3);
}
| WORD EQUALS QUOTE
{
#ifdef ERROR_STUFF
fprintf(stderr, "variable_definition: %s = \"%s\"\n", $1, $3);
#endif
strcat(sub_locals, ",");
strcat(sub_locals, $1);
strcat(sub_locals, "=\"");
strcat(sub_locals, $3);
strcat(sub_locals, "\"");
dbmfFree($1); dbmfFree($3);
}
| QUOTE EQUALS QUOTE
{
#ifdef ERROR_STUFF
fprintf(stderr, "variable_definition: \"%s\" = \"%s\"\n", $1, $3);
#endif
strcat(sub_locals, ",\"");
strcat(sub_locals, $1);
strcat(sub_locals, "\"=\"");
strcat(sub_locals, $3);
strcat(sub_locals, "\"");
dbmfFree($1); dbmfFree($3);
}
;
%%
#include "dbLoadTemplate_lex.c"
static int yyerror(char* str)
{
if (str)
fprintf(stderr, "Substitution file error: %s\n", str);
else
fprintf(stderr, "Substitution file error.\n");
fprintf(stderr, "line %d: '%s'\n", line_num, yytext);
return 0;
}
static int is_not_inited = 1;
int dbLoadTemplate(const char *sub_file, const char *cmd_collect, const char *path)
{
FILE *fp;
int i;
line_num = 1;
if (!sub_file || !*sub_file) {
fprintf(stderr, "must specify variable substitution file\n");
return -1;
}
if (dbTemplateMaxVars < 1) {
fprintf(stderr,"Error: dbTemplateMaxVars = %d, must be positive\n",
dbTemplateMaxVars);
return -1;
}
fp = fopen(sub_file, "r");
if (!fp && sub_file[0] != OSI_PATH_SEPARATOR[0]) {
const char *dirname, *end;
int dirlen;
char* filename;
if (!path || !*path) {
path = getenv("EPICS_DB_INCLUDE_PATH");
}
for(dirname = path; dirname != NULL; dirname = end) {
end = strchr(dirname, OSI_PATH_LIST_SEPARATOR[0]);
if (end) dirlen = (int)(end++ - dirname);
else dirlen = (int)strlen(dirname);
if (dirlen == 0) continue; /* ignore empty path elements */
if (dirlen == 1 && dirname[0] == '.') continue; /* we had . already */
filename = NULL;
if (asprintf(&filename, "%.*s" OSI_PATH_SEPARATOR "%s", dirlen, dirname, sub_file) < 0)
{
fprintf(stderr,"dbLoadTemplate: out of memory\n");
break;
}
fp = fopen(filename, "r");
free(filename);
if (fp) break;
}
}
if (!fp) {
fprintf(stderr, "dbLoadTemplate: error opening sub file %s: %s\n", sub_file, strerror(errno));
return -1;
}
vars = malloc(dbTemplateMaxVars * sizeof(char*));
sub_collect = malloc(dbTemplateMaxVars * MAX_VAR_FACTOR);
if (!vars || !sub_collect) {
free(vars);
free(sub_collect);
fclose(fp);
fprintf(stderr, "dbLoadTemplate: Out of memory!\n");
return -1;
}
strcpy(sub_collect, ",");
if (cmd_collect && *cmd_collect) {
strcat(sub_collect, cmd_collect);
sub_locals = sub_collect + strlen(sub_collect);
} else {
sub_locals = sub_collect;
*sub_locals = '\0';
}
var_count = 0;
sub_count = 0;
if (is_not_inited) {
yyin = fp;
is_not_inited = 0;
} else {
yyrestart(fp);
}
yyparse();
for (i = 0; i < var_count; i++) {
dbmfFree(vars[i]);
}
free(vars);
free(sub_collect);
vars = NULL;
fclose(fp);
if (db_file_name) {
dbmfFree(db_file_name);
db_file_name = NULL;
}
return 0;
}
#ifndef EPICS_3_13
#include "registry.h"
epicsExportAddress(int, dbTemplateMaxVars);
static const iocshFuncDef dbLoadTemplateDef = {
"dbLoadTemplate", 3, (const iocshArg *[]) {
&(iocshArg) { "filename", iocshArgString },
&(iocshArg) { "\"macro=value,...\"", iocshArgString },
&(iocshArg) { "searchpath", iocshArgString },
}};
#ifdef __GNUC__
/* Without this I always get the original dbLoadTemplate linked instead of my version */
int __dbLoadTemplate(const char *sub_file, const char *cmd_collect, const char *path) __attribute__ ((alias ("dbLoadTemplate")));
#define dbLoadTemplate __dbLoadTemplate
#endif
static void dbLoadTemplateFunc(const iocshArgBuf *args)
{
dbLoadTemplate(args[0].sval, args[1].sval, args[2].sval);
}
typedef struct iocshCommand {
iocshFuncDef const *pFuncDef;
iocshCallFunc func;
struct iocshCommand *next;
}iocshCommand;
static void dbLoadTemplateRegister(void)
{
static int firstTime = 1;
if (firstTime) {
iocshRegister(&dbLoadTemplateDef, dbLoadTemplateFunc);
firstTime = 0;
}
}
epicsExportRegistrar(dbLoadTemplateRegister);
#endif
/*************************************************************************\
* Copyright (c) 2006 UChicago, as Operator of Argonne
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
newline "\n"
backslash "\\"
doublequote "\""
singlequote "'"
comment "#"
whitespace [ \t\r]
escape {backslash}.
dstringchar [^"\n\\]
sstringchar [^'\n\\]
/*
This breaks backward compatibility to 3.14.8:
bareword [a-zA-Z0-9_\-+:./\\\[\]<>;]
Re-enable unquoted chars except #
*/
bareword [a-zA-Z0-9_\-+:./\\\[\]<>;^~*%!|&$()@?]
%%
"pattern" { return(PATTERN); }
"file" { return(DBFILE); }
"global" { return(GLOBAL); }
{doublequote}({dstringchar}|{escape})*{doublequote} |
{singlequote}({sstringchar}|{escape})*{singlequote} {
yylval.Str = dbmfStrdup(yytext+1);
yylval.Str[strlen(yylval.Str)-1] = '\0';
return(QUOTE);
}
{bareword}+ {
yylval.Str = dbmfStrdup(yytext);
return(WORD);
}
"=" { return(EQUALS); }
"," { return(COMMA); }
"{" { return(O_BRACE); }
"}" { return(C_BRACE); }
{comment}.* ;
{whitespace} ;
{newline} { line_num++; }
. {
char message[40];
sprintf(message, "invalid character '%c'", yytext[0]);
yyerror(message);
/* Suppress compiler warning messages */
if (0) yyunput('c',NULL);
if (0) yy_switch_to_buffer(NULL);
}
%%
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "expr.h"
int exprDebug;
static int parseSubExpr(const char **pp, long *v, int pr, int op);
static long parseString(const char **pp, const char **pstart);
#define parseExpr(pp,v) parseSubExpr(pp, v, 0, 0)
#define skipSpace(p) while (isspace((unsigned char)*p)) p++
static int parseValue(const char **pp, long *v)
{
long val;
const char *p = *pp;
char o;
/* A value is optionally prefixed with an unary operator + - ! ~.
* It is either a number (decimal, octal or hex)
* or an expression in ().
* Allowed chars after a number: operators, closing parenthesis, whitespace, quotes, end of string
*/
/* first look for value */
skipSpace(p);
o = *p;
if (memchr("+-~!", o, 4))
{
/* unary operators */
p++;
if (!parseValue(&p, &val)) return 0; /* no valid value */
if (exprDebug) printf("parseValue: %c %ld\n", o, val);
if (o == '-') val=-val;
else if (o == '~') val=~val;
else if (o == '!') val=!val;
}
else if (o == '(')
{
/* sub-expression */
p++;
if (parseExpr(&p, &val) < 0) return 0; /* no valid expression */
skipSpace(p);
if (*p++ != ')') return 0; /* missing ) */
}
else if (o == '#')
{
/* string length operator */
p++;
if (exprDebug) printf("parseValue: string length of %s\n", p);
if (*p == '"' || *p == '\'')
val = parseString(&p, NULL);
else return 0;
}
else
{
/* number */
char *e;
val = strtol(p, &e, 0);
if (e == p) return 0; /* no number */
if (isalpha((unsigned char)*e)||*e=='.')
{
/* part of plain word or floating point number */
return 0;
}
p = e;
}
*pp = p;
*v = val;
return 1;
}
static long ipow(long base, long exp)
{
long val;
if (exp == 2) return base*base;
if (exp == 3) return base*base*base;
if (exp == 1) return base;
if (exp == 0) return 1;
if (exp < 0) return 0;
val = ipow(base,exp>>1);
val *= val;
if (exp&1) val *= base;
return val;
}
static char parseSep(const char **pp, const char *seps)
{
const char *p = *pp;
skipSpace(p);
if (!*p || !strchr(seps, *p)) return 0;
*pp = p+1;
return *p;
}
struct {char str[4]; int pr;} ops[] = {
{":=",0},
{"**",14},
{"*", 13},{"/",13},{"%",13},
{"+",12},{"-",12},
{"<<",11},{">>>",11},{">>",11},
{"<?",10},{">?",10},
{"<=>",9},
{"<=",8},{">=",8},{"<",8},{">",8},
{"==",7},{"!=",7},
{"&&",3},{"||",2},
{"&",6},{"^",5},{"|",4},
{"?:",1},{"?",1}
};
enum op {
op_none,
op_pow,
op_mul,op_div,op_mod,
op_plus,op_minus,
op_lshift,op_urshift,op_rshift,
op_min,op_max,
op_cmp,
op_lteq,op_gteq,op_lt,op_gt,
op_eq,op_neq,
op_logicand,op_logicor,
op_bitand,op_bitxor,op_bitor,
op_alt,op_if
};
static int startsWith(const char *p, const char *s)
{
int i = 0;
while (*s) { i++; if (*p++ != *s++) return 0; }
return i;
}
static int parseOp(const char **pp)
{
const char *p = *pp;
int o, l;
skipSpace(p);
if (ispunct((unsigned char)*p))
{
for (o = 1; o < (int)(sizeof(ops)/sizeof(ops[0])); o++)
{
if ((l = startsWith(p, ops[o].str)))
{
/* operator found */
*pp = p+l;
return o;
}
}
}
return 0;
}
static int parseSubExpr(const char **pp, long *v, int pr, int o)
{
const char *p = *pp;
long val = o ? *v : 0;
long val2;
int o2 = o;
if (exprDebug) printf("parseExpr(%d): start %ld %s \"%s\"\n", pr, val, ops[o].str, p);
do {
if (!parseValue(&p, &val2))
{
if (exprDebug) printf("parseExpr(%d): no value after %ld %s\n", pr, val, ops[o].str);
return -1;
}
nextop:
if ((o2 = parseOp(&p)))
{
if (exprDebug) printf("parseExpr(%d): %ld %s %ld %s \"%s\"\n", pr, val, ops[o].str, val2, ops[o2].str, p);
if (o && ops[o2].pr > ops[o].pr)
{
if ((o2 = parseSubExpr(&p, &val2, ops[o].pr, o2)) < 0)
{
if (exprDebug) printf("parseExpr(%d): parse failed after %ld %s %ld\n", pr, val, ops[o].str, val2);
return -1;
}
}
}
if (exprDebug) printf("parseExpr(%d): calc %ld %s %ld\n", pr, val, ops[o].str, val2);
switch (o)
{
case op_none: val = val2; break;
case op_pow: val = ipow(val, val2); break;
case op_mul: val *= val2; break;
case op_div: val /= val2; break;
case op_mod: val %= val2; break;
case op_plus: val += val2; break;
case op_minus: val -= val2; break;
case op_lshift: val <<= val2; break;
case op_urshift: val = (unsigned long)val >> val2; break;
case op_rshift: val >>= val2; break;
case op_min: if (val2 < val) val = val2; break;
case op_max: if (val2 > val) val = val2; break;
case op_cmp: val = val < val2 ? -1 : val == val2 ? 0 : 1; break;
case op_lteq: val = val <= val2; break;
case op_gteq: val = val >= val2; break;
case op_lt: val = val < val2; break;
case op_gt: val = val > val2; break;
case op_eq: val = val == val2; break;
case op_neq: val = val != val2; break;
case op_logicand: val = val && val2; break;
case op_logicor: val = val || val2; break;
case op_bitand: val &= val2; break;
case op_bitxor: val ^= val2; break;
case op_bitor: val |= val2; break;
case op_alt: if (!val) val = val2; break;
}
if (exprDebug) printf("parseExpr(%d): result %ld\n", pr, val);
if (o2 == op_if)
{
long val3 = 0;
val2 = 1;
if (exprDebug) printf("parseExpr(%d) if %ld\n", pr, val);
if ((o2 = parseExpr(&p, &val2)) >= 0)
{
if (exprDebug) printf("parseExpr(%d) then %ld\n", pr, val2);
if (parseSep(&p, ":"))
{
parseExpr(&p, &val3);
if (exprDebug) printf("parseExpr(%d) else %ld\n", pr, val3);
}
}
if (exprDebug) printf("parseExpr(%d) if %ld then %ld else %ld\n", pr, val, val2, val3);
val = val ? val2 : val3;
if (exprDebug) printf("parseExpr(%d): result %ld, o2=%d, rest \"%s\" \n", pr, val, o2, p);
if (o2 == -1) goto nextop;
}
o = o2;
} while (ops[o].pr && pr <= ops[o].pr);
if (exprDebug) printf("parseExpr(%d): value = %ld return %d %s\n", pr, val, o, ops[o].str);
*pp = p;
*v = val;
return o;
}
static const char *getFormat(const char **pp)
{
static char format [20];
const char *p = *pp;
unsigned int i = 1;
if (exprDebug) printf("getFormat %s\n", p);
if ((format[0] = *p++) == '%')
{
while (i < sizeof(format) && memchr(" #-+0", *p, 5))
format[i++] = *p++;
while (i < sizeof(format) && *p >= '0' && *p <= '9')
format[i++] = *p++;
if (i < sizeof(format))
format[i++] = 'l';
if (i < sizeof(format) && memchr("diouxXc", *p, 7))
{
format[i++] = *p++;
format[i] = 0;
*pp = p;
if (exprDebug) printf("format = '%s'\n", format);
return format;
}
}
if (exprDebug) printf("no format\n");
return NULL;
}
static int parseSlice(const char **pp, long* pstart, long* plength)
{
const char *p = *pp;
long slice_start = 0;
long slice_length = 0;
long string_length = *plength;
char o;
if (*p++ != '[') return 0;
parseExpr(&p, &slice_start);
if (slice_start < 0) slice_start += string_length;
if ((o = parseSep(&p, ":,"))) /* [start,length] or [start:end] */
{
parseExpr(&p, &slice_length);
if (o == ':')
{
if (slice_length < 0) slice_length += string_length;
slice_length -= slice_start;
}
}
else slice_length = 1;
if (slice_start < 0)
{
slice_length += slice_start;
slice_start = 0;
}
if (slice_start > string_length)
slice_length = 0;
if (slice_length > string_length - slice_start)
slice_length = string_length - slice_start;
if (slice_length < 0)
slice_length = 0;
skipSpace(p);
if (*p++ != ']') return 0;
*pstart += slice_start;
*plength = slice_length;
*pp = p;
return 1;
}
static long parseString(const char **pp, const char **pstart)
{
const char *p = *pp;
const char *string_start = p;
long slice_start = 0;
long length = 0;
char q;
q = *p++;
while (*p) /* string length with escapes */
{
if (*p == '\\')
if (*++p == 0) break;
if (*p++ == q) break;
length++;
}
while (parseSlice(&p, &slice_start, &length));
if (exprDebug) printf("parseString %.*s[%ld,%ld]\n", (int)(p-string_start), string_start, slice_start, length);
if (length && pstart)
{
while (slice_start-- > 0)
{
if (*string_start++ == '\\') string_start++;
}
*pstart = ++string_start;
}
*pp = p;
return length;
}
static void writeString(char** pw, const char* r, long length, char q)
{
char* w = *pw;
*w++ = q;
while (length-- > 0)
{
if (*r == '\\') *w++ = *r++;
*w++ = *r++;
}
*w++ = q;
*w++ = 0;
*pw = w;
}
size_t replaceExpressions(const char *r, char *buffer, size_t buffersize)
{
long val, string_length;
char *w = buffer;
char *s;
const char *string_start = r;
char q;
*w = 0;
while (*r)
{
s = w;
if (*r == '"' || *r == '\'')
{
/* quoted strings */
q = *r;
string_length = parseString(&r, &string_start);
writeString(&w, string_start, string_length, q);
if (exprDebug) printf("quoted string %s\n", s);
}
else if (*r == '%')
{
/* formatted expression */
const char *r2 = r;
const char *f;
if (exprDebug) printf("formatted expression after '%s'\n", s);
if ((f = getFormat(&r2)) && parseExpr(&r2, &val) >= 0)
{
r = r2;
if (w > buffer && w[-1] == '(' && *r2++ == ')')
{
w--;
r = r2;
}
w += sprintf(w, f , val);
if (exprDebug) printf("formatted expression %s\n", s);
}
}
else if (parseExpr(&r, &val) >= 0)
{
/* unformatted expression */
if (r[-1] == '?' && (q = parseSep(&r, "\"'")))
{
/* handle expression ? "string1" : "string2" */
w = s;
r--;
string_length = parseString(&r, &string_start);
if (val) writeString(&w, string_start, string_length, q);
if (parseSep(&r, ":"))
{
string_length = parseString(&r, &string_start);
if (!val) writeString(&w, string_start, string_length, q);
}
}
else
{
w += sprintf(w, "%ld", val);
}
if (exprDebug) printf("expression %s\n", s);
}
else {
/* unquoted string (i.e plain word) */
do {
if ((*w++ = *r++) == '\\') if (*r) *w++ = *r++;
} while (*r && !strchr("%(\"', \t\n", *r));
*w = 0;
if (exprDebug) printf("plain word '%s'\n", s);
}
/* copy space */
while (isspace((unsigned char)*r) || *r == ',') *w++ = *r++;
/* terminate */
*w = 0;
}
return w - buffer;
}
#ifndef expr_h
#define expr_h
#ifdef __cplusplus
extern {
#endif
extern int exprDebug;
size_t replaceExpressions(const char* source, char* buffer, size_t buffersize);
/* Resolve integer expressions that are either free standing
* or in parentheses () embedded in an unquoted word.
* Do not resolve expressions in single or double quoted strings.
* An expression optionally starts with a integer format such as %x.
* It consists of integer numbers, operators and parentheses ().
*/
#ifdef __cplusplus
}
#endif
#endif
/*
* ld - load code dynamically
*
* $Author: zimoch $
* $ID$
* $Date: 2015/06/29 09:47:30 $
*
* DISCLAIMER: Use at your own risc and so on. No warranty, no refund.
*/
#ifdef __unix
/* for vasprintf and dl_iterate_phdr */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#endif
/* for 64 bit (NFS) file systems */
#define _FILE_OFFSET_BITS 64
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <recSup.h>
#include <initHooks.h>
#include <osiFileName.h>
#include <epicsVersion.h>
#include <iocsh.h>
#include <dbAccess.h>
/* This prototype is missing in older EPICS versions */
epicsShareFunc int epicsShareAPI iocshCmd(const char *cmd);
#include <epicsExit.h>
#include <epicsStdio.h>
#include <osiFileName.h>
#include <epicsExport.h>
#include "version.h"
#include "require.h"
int requireDebug;
#if defined(__unix)
#ifndef OS_CLASS
#ifdef __linux
#define OS_CLASS "Linux"
#endif
#ifdef SOLARIS
#define OS_CLASS "solaris"
#endif
#ifdef __rtems__
#define OS_CLASS "RTEMS"
#endif
#ifdef CYGWIN32
#define OS_CLASS "cygwin32"
#endif
#ifdef freebsd
#define OS_CLASS "freebsd"
#endif
#ifdef darwin
#define OS_CLASS "Darwin"
#endif
#ifdef _AIX32
#define OS_CLASS "AIX"
#endif
#endif
#include <dlfcn.h>
#define HMODULE void *
#define getAddress(module, name) dlsym(module, name)
#ifdef CYGWIN32
#define PREFIX
#define INFIX
#define EXT ".dll"
#else
#define PREFIX "lib"
#define INFIX
#define EXT ".so"
#endif
#elif defined(_WIN32)
#ifndef OS_CLASS
#define OS_CLASS "WIN32"
#endif
#include <windows.h>
#include <Psapi.h>
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "psapi.lib")
#include "asprintf.h"
#define snprintf _snprintf
#define setenv(name, value, overwrite) _putenv_s(name, value)
#define PATH_MAX MAX_PATH
#define PREFIX
#define INFIX
#define EXT ".dll"
#define getAddress(module, name) GetProcAddress(module, name)
static char *realpath(const char *path, char *buffer)
{
int len = MAX_PATH;
if (buffer == NULL)
{
len = GetFullPathName(path, 0, NULL, NULL);
if (len == 0)
return NULL;
buffer = malloc(len);
if (buffer == NULL)
return NULL;
}
GetFullPathName(path, len, buffer, NULL);
return buffer;
}
#else
#warning unknown OS
#define PREFIX
#define INFIX
#define EXT
#define getAddress(module, name) NULL
#endif
/* for readdir: Windows or Posix */
#if defined(_WIN32)
#define DIR_HANDLE HANDLE
#define DIR_ENTRY WIN32_FIND_DATA
#define IF_OPEN_DIR(f) if (snprintf(f + modulediroffs, sizeof(f) - modulediroffs, "\\*.*"), (dir = FindFirstFile(filename, &direntry)) != INVALID_HANDLE_VALUE || (FindClose(dir), 0))
#define START_DIR_LOOP do
#define END_DIR_LOOP \
while (FindNextFile(dir, &direntry)) \
; \
FindClose(dir);
#define SKIP_NON_DIR(e) \
if (!(e.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || (e.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) \
continue;
#define FILENAME(e) e.cFileName
#else
#include <dirent.h>
#define DIR_HANDLE DIR *
#define IF_OPEN_DIR(f) if ((dir = opendir(f)))
#define DIR_ENTRY struct dirent *
#define START_DIR_LOOP while ((errno = 0, direntry = readdir(dir)) != NULL)
#define END_DIR_LOOP \
if (!direntry && errno) \
fprintf(stderr, "error reading directory %s: %s\n", filename, strerror(errno)); \
if (dir) \
closedir(dir);
#ifdef _DIRENT_HAVE_D_TYPE
#define SKIP_NON_DIR(e) \
if (e->d_type != DT_DIR && e->d_type != DT_UNKNOWN) \
continue;
#else
#define SKIP_NON_DIR(e)
#endif
#define FILENAME(e) e->d_name
#endif
#define LIBDIR "lib" OSI_PATH_SEPARATOR
#define TEMPLATEDIR "db"
/*
#define TOSTR(s) TOSTR2(s)
#define TOSTR2(s) #s
const char epicsRelease[] = TOSTR(EPICS_VERSION)"."TOSTR(EPICS_REVISION)"."TOSTR(EPICS_MODIFICATION);
#ifndef T_A
#error T_A not defined: Compile with USR_CFLAGS += -DT_A='"${T_A}"'
#endif
const char targetArch[] = T_A;
*/
#ifndef OS_CLASS
#error OS_CLASS not defined: Try to compile with USR_CFLAGS += -DOS_CLASS='"${OS_CLASS}"'
#endif
const char osClass[] = OS_CLASS;
/* loadlib (library)
Find a loadable library by name and load it.
*/
char epicsRelease[80];
char *targetArch;
void set_require_env()
{
char *epics_version_major = getenv("EPICS_VERSION_MAJOR");
char *epics_version_middle = getenv("EPICS_VERSION_MIDDLE");
char *epics_version_minor = getenv("EPICS_VERSION_MINOR");
sprintf(epicsRelease, "%s.%s.%s", epics_version_major, epics_version_middle, epics_version_minor);
targetArch = getenv("EPICS_HOST_ARCH");
return;
}
static HMODULE loadlib(const char *libname)
{
HMODULE libhandle = NULL;
if (libname == NULL)
{
fprintf(stderr, "missing library name\n");
return NULL;
}
#if defined(__unix)
if ((libhandle = dlopen(libname, RTLD_NOW | RTLD_GLOBAL)) == NULL)
{
fprintf(stderr, "Loading %s library failed: %s\n",
libname, dlerror());
}
#elif defined(_WIN32)
if ((libhandle = LoadLibrary(libname)) == NULL)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
fprintf(stderr, "Loading %s library failed: %s\n",
libname, lpMsgBuf);
LocalFree(lpMsgBuf);
}
#else
fprintf(stderr, "cannot load libraries on this OS.\n");
#endif
return libhandle;
}
typedef struct moduleitem
{
struct moduleitem *next;
char content[0];
} moduleitem;
static moduleitem *loadedModules = NULL;
static unsigned long moduleCount = 0;
static unsigned long moduleListBufferSize = 1;
static unsigned long maxModuleNameLength = 0;
int putenvprintf(const char *format, ...)
{
va_list ap;
char *var;
char *val;
int status = 0;
if (!format)
return -1;
va_start(ap, format);
if (vasprintf(&var, format, ap) < 0)
{
perror("require putenvprintf");
return errno;
}
va_end(ap);
if (requireDebug)
printf("require: putenv(\"%s\")\n", var);
val = strchr(var, '=');
if (!val)
{
fprintf(stderr, "putenvprintf: string contains no =: %s\n", var);
status = -1;
}
else
{
*val++ = 0;
if (setenv(var, val, 1) != 0)
{
perror("require putenvprintf: setenv failed");
status = errno;
}
}
free(var);
return status;
}
void pathAdd(const char *varname, const char *dirname)
{
char *old_path;
if (!varname || !dirname)
{
fprintf(stderr, "usage: pathAdd \"ENVIRONMENT_VARIABLE\",\"directory\"\n");
fprintf(stderr, " Adds or moves the directory to the front of the ENVIRONMENT_VARIABLE\n");
fprintf(stderr, " but after a leading \".\".\n");
return;
}
/* add directory to front */
old_path = getenv(varname);
if (old_path == NULL)
putenvprintf("%s=." OSI_PATH_LIST_SEPARATOR "%s", varname, dirname);
else
{
size_t len = strlen(dirname);
char *p;
/* skip over "." at the beginning */
if (old_path[0] == '.' && old_path[1] == OSI_PATH_LIST_SEPARATOR[0])
old_path += 2;
/* If directory is already in path, move it to front */
p = old_path;
while ((p = strstr(p, dirname)) != NULL)
{
if ((p == old_path || *(p - 1) == OSI_PATH_LIST_SEPARATOR[0]) &&
(p[len] == 0 || p[len] == OSI_PATH_LIST_SEPARATOR[0]))
{
if (p == old_path)
break; /* already at front, nothing to do */
memmove(old_path + len + 1, old_path, p - old_path - 1);
strcpy(old_path, dirname);
old_path[len] = OSI_PATH_LIST_SEPARATOR[0];
if (requireDebug)
printf("require: modified %s=%s\n", varname, old_path);
break;
}
p += len;
}
if (p == NULL)
/* add new directory to the front (after "." )*/
putenvprintf("%s=." OSI_PATH_LIST_SEPARATOR "%s" OSI_PATH_LIST_SEPARATOR "%s",
varname, dirname, old_path);
}
}
static int setupDbPath(const char *module, const char *dbdir)
{
char *absdir = realpath(dbdir, NULL); /* so we can change directory later safely */
if (absdir == NULL)
{
if (requireDebug)
printf("require: cannot resolve %s\n", dbdir);
return -1;
}
if (requireDebug)
printf("require: found template directory %s\n", absdir);
/* set up db search path environment variables
<module>_TEMPLATES template path of <module>
<module>_DB template path of <module>
TEMPLATES template path of the current module (overwritten)
EPICS_DB_INCLUDE_PATH template path of all loaded modules (last in front after ".")
*/
putenvprintf("%s_DB=%s", module, absdir);
putenvprintf("%s_TEMPLATES=%s", module, absdir);
putenvprintf("TEMPLATES=%s", absdir);
pathAdd("EPICS_DB_INCLUDE_PATH", absdir);
free(absdir);
return 0;
}
static int getRecordHandle(const char *namepart, short type, long minsize, DBADDR *paddr)
{
/*
#define PVNAME_STRINGSZ 61
defined in EPICS_BASE/include/dbDefs.h
*/
char recordname[PVNAME_STRINGSZ] = "";
long dummy = 0L;
long offset = 0L;
sprintf(recordname, "%.*s%s", (int)(PVNAME_STRINGSZ - strlen(namepart) - 1), getenv("REQUIRE_IOC"), namepart);
if (dbNameToAddr(recordname, paddr) != 0)
{
fprintf(stderr,
"require:getRecordHandle : record %s not found\n",
recordname);
return -1;
}
if (paddr->field_type != type)
{
fprintf(stderr,
"require:getRecordHandle : record %s has wrong type %s instead of %s\n",
recordname,
pamapdbfType[paddr->field_type].strvalue,
pamapdbfType[type].strvalue);
return -1;
}
if (paddr->no_elements < minsize)
{
fprintf(stderr,
"require:getRecordHandle : record %s has not enough elements: %lu instead of %lu\n",
recordname,
paddr->no_elements,
minsize);
return -1;
}
if (paddr->pfield == NULL)
{
fprintf(stderr, "require:getRecordHandle : record %s has not yet allocated memory\n",
recordname);
return -1;
}
/* update array information */
dbGetRset(paddr)->get_array_info(paddr, &dummy, &offset);
return 0;
}
/*
We can fill the records only after they have been initialized, at initHookAfterFinishDevSup.
But use double indirection here because in 3.13 we must
wait until initHooks is loaded before we can register the hook.
*/
static void fillModuleListRecord(initHookState state)
{
if (state == initHookAfterFinishDevSup) /* MODULES record exists and has allocated memory */
{
DBADDR modules, versions, modver;
int have_modules, have_versions, have_modver;
moduleitem *m;
int i = 0;
long c = 0;
if (requireDebug)
printf("require: fillModuleListRecord\n");
have_modules = (getRecordHandle(":MODULES", DBF_STRING, moduleCount, &modules) == 0);
have_versions = (getRecordHandle(":VERSIONS", DBF_STRING, moduleCount, &versions) == 0);
moduleListBufferSize += moduleCount * maxModuleNameLength;
have_modver = (getRecordHandle(":MOD_VER", DBF_CHAR, moduleListBufferSize, &modver) == 0);
for (m = loadedModules, i = 0; m; m = m->next, i++)
{
size_t lm = strlen(m->content) + 1;
if (have_modules)
{
if (requireDebug)
printf("require: %s[%d] = \"%.*s\"\n",
modules.precord->name, i,
MAX_STRING_SIZE - 1, m->content);
sprintf((char *)(modules.pfield) + i * MAX_STRING_SIZE, "%.*s",
MAX_STRING_SIZE - 1, m->content);
}
if (have_versions)
{
if (requireDebug)
printf("require: %s[%d] = \"%.*s\"\n",
versions.precord->name, i,
MAX_STRING_SIZE - 1, m->content + lm);
sprintf((char *)(versions.pfield) + i * MAX_STRING_SIZE, "%.*s",
MAX_STRING_SIZE - 1, m->content + lm);
}
if (have_modver)
{
if (requireDebug)
printf("require: %s+=\"%-*s%s\"\n",
modver.precord->name,
(int)maxModuleNameLength, m->content, m->content + lm);
c += sprintf((char *)(modver.pfield) + c, "%-*s%s\n",
(int)maxModuleNameLength, m->content, m->content + lm);
}
}
if (have_modules)
dbGetRset(&modules)->put_array_info(&modules, i);
if (have_versions)
dbGetRset(&versions)->put_array_info(&versions, i);
if (have_modver)
dbGetRset(&modver)->put_array_info(&modver, c + 1);
}
}
void registerModule(const char *module, const char *version, const char *location)
{
moduleitem *m, **pm;
size_t lm = strlen(module) + 1;
size_t lv = (version ? strlen(version) : 0) + 1;
size_t ll = 1;
char *abslocation = NULL;
char *argstring = NULL;
int addSlash = 0;
const char *mylocation;
static int firstTime = 1;
if (requireDebug)
printf("require: registerModule(%s,%s,%s)\n", module, version, location);
if (firstTime)
{
initHookRegister(fillModuleListRecord);
if (requireDebug)
printf("require: initHookRegister\n");
firstTime = 0;
}
if (!version)
version = "";
if (location)
{
abslocation = realpath(location, NULL);
if (!abslocation)
abslocation = (char *)location;
ll = strlen(abslocation) + 1;
/* linux realpath removes trailing slash */
if (abslocation[ll - 1 - strlen(OSI_PATH_SEPARATOR)] != OSI_PATH_SEPARATOR[0])
{
addSlash = strlen(OSI_PATH_SEPARATOR);
}
}
m = (moduleitem *)malloc(sizeof(moduleitem) + lm + lv + ll + addSlash);
if (m == NULL)
{
fprintf(stderr, "require: out of memory\n");
return;
}
m->next = NULL;
strcpy(m->content, module);
strcpy(m->content + lm, version);
strcpy(m->content + lm + lv, abslocation ? abslocation : "");
if (addSlash)
strcpy(m->content + lm + lv + ll - 1, OSI_PATH_SEPARATOR);
if (abslocation != location)
free(abslocation);
for (pm = &loadedModules; *pm != NULL; pm = &(*pm)->next)
;
*pm = m;
if (lm > maxModuleNameLength)
maxModuleNameLength = lm;
moduleListBufferSize += lv;
moduleCount++;
putenvprintf("MODULE=%s", module);
putenvprintf("%s_VERSION=%s", module, version);
if (location)
{
putenvprintf("%s_DIR=%s", module, m->content + lm + lv);
pathAdd("SCRIPT_PATH", m->content + lm + lv);
}
/* only do registration register stuff at init */
if (interruptAccept)
return;
/* create a record with the version string */
mylocation = getenv("require_DIR");
if (mylocation == NULL)
return;
if (asprintf(&abslocation, "%s" OSI_PATH_SEPARATOR "db" OSI_PATH_SEPARATOR "moduleversion.template", mylocation) < 0)
return;
/*
Require DB has the following four PVs:
- $(REQUIRE_IOC):$(MODULE)_VER
- $(REQUIRE_IOC):MOD_VER
- $(REQUIRE_IOC):VERSIONS
- $(REQUIRE_IOC):MODULES
We reserved 30 chars for :$(MODULE)_VER, so MODULE has the maximum 24 chars.
And we've reserved for 30 chars for $(REQUIRE_IOC).
So, the whole PV and record name in moduleversion.template has 59 + 1.
*/
if (asprintf(&argstring, "REQUIRE_IOC=%.30s, MODULE=%.24s, VERSION=%.39s, MODULE_COUNT=%lu, BUFFER_SIZE=%lu",
getenv("REQUIRE_IOC"), module, version, moduleCount,
moduleListBufferSize + maxModuleNameLength * moduleCount) < 0)
return;
printf("Loading module info records for %s\n", module);
dbLoadRecords(abslocation, argstring);
free(argstring);
free(abslocation);
}
#if defined(__linux)
/* This is the Linux link.h, not the EPICS link.h ! */
#include <link.h>
static int findLibRelease(
struct dl_phdr_info *info, /* shared library info */
size_t size, /* size of info structure */
void *data /* user-supplied arg */
)
{
void *handle;
char *location = NULL;
char *p;
char *version;
char *symname;
char name[PATH_MAX + 11]; /* get space for library path + "LibRelease" */
(void)data; /* unused */
if (size < sizeof(struct dl_phdr_info))
return 0; /* wrong version of struct dl_phdr_info */
/* find a symbol with a name like "_<module>LibRelease"
where <module> is from the library name "<location>/lib<module>.so" */
if (info->dlpi_name == NULL || info->dlpi_name[0] == 0)
return 0; /* no library name */
strcpy(name, info->dlpi_name); /* get a modifiable copy of the library name */
handle = dlopen(info->dlpi_name, RTLD_LAZY); /* re-open already loaded library */
p = strrchr(name, '/'); /* find file name part in "<location>/lib<module>.so" */
if (p)
{
location = name;
*++p = 0;
}
else
p = name; /* terminate "<location>/" (if exists) */
*(symname = p + 2) = '_'; /* replace "lib" with "_" */
p = strchr(symname, '.'); /* find ".so" extension */
if (p == NULL)
p = symname + strlen(symname); /* no file extension ? */
strcpy(p, "LibRelease"); /* append "LibRelease" to module name */
version = dlsym(handle, symname); /* find symbol "_<module>LibRelease" */
if (version)
{
*p = 0;
symname++; /* get "<module>" from "_<module>LibRelease" */
if ((p = strstr(name, "/" LIBDIR)) != NULL)
p[1] = 0; /* cut "<location>" before LIBDIR */
if (getLibVersion(symname) == NULL)
registerModule(symname, version, location);
}
dlclose(handle);
return 0;
}
static void registerExternalModules()
{
/* iterate over all loaded libraries */
dl_iterate_phdr(findLibRelease, NULL);
}
#elif defined(_WIN32)
static void registerExternalModules()
{
HMODULE hMods[100];
HANDLE hProcess = GetCurrentProcess();
DWORD cbNeeded;
char *location = NULL;
char *p;
char *version;
char *symname;
unsigned int i;
char name[MAX_PATH + 11]; /* get space for library path + "LibRelease" */
/* iterate over all loaded libraries */
if (!EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
return;
for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
{
/* Get the full path to the module's file. */
if (!GetModuleFileName(hMods[i], name, MAX_PATH))
continue; /* no library name */
name[sizeof(name) - 1] = 0; /* WinXP may not terminate the string */
p = strrchr(name, '\\'); /* find file name part in "<location>/<module>.dll" */
if (p)
{
location = name;
}
else
p = name; /* find end of "<location>\\" (if exists) */
symname = p;
p = strchr(symname, '.'); /* find ".dll" */
if (p == NULL)
p = symname + strlen(symname); /* no file extension ? */
memmove(symname + 2, symname, p - symname); /* make room for 0 and '_' */
*symname++ = 0; /* terminate "<location>/" */
*symname = '_'; /* prefix module name with '_' */
strcpy((p += 2), "LibRelease"); /* append "LibRelease" to module name */
version = (char *)GetProcAddress(hMods[i], symname); /* find symbol "_<module>LibRelease" */
if (version)
{
*p = 0;
symname++; /* get "<module>" from "_<module>LibRelease" */
if ((p = strstr(name, "\\" LIBDIR)) != NULL)
p[1] = 0; /* cut "<location>" before LIBDIR */
if (getLibVersion(symname) == NULL)
registerModule(symname, version, location);
}
}
}
#else
static void registerExternalModules()
{
;
}
#endif
size_t foreachLoadedLib(size_t (*func)(const char *name, const char *version, const char *path, void *arg), void *arg)
{
moduleitem *m;
int result;
for (m = loadedModules; m; m = m->next)
{
const char *name = m->content;
const char *version = name + strlen(name) + 1;
const char *path = version + strlen(version) + 1;
result = func(name, version, path, arg);
if (result)
return result;
}
return 0;
}
const char *getLibVersion(const char *libname)
{
moduleitem *m;
for (m = loadedModules; m; m = m->next)
{
if (strcmp(m->content, libname) == 0)
{
return m->content + strlen(m->content) + 1;
}
}
return NULL;
}
const char *getLibLocation(const char *libname)
{
moduleitem *m;
char *v;
for (m = loadedModules; m; m = m->next)
{
if (strcmp(m->content, libname) == 0)
{
v = m->content + strlen(m->content) + 1;
return v + strlen(v) + 1;
}
}
return NULL;
}
int libversionShow(const char *outfile)
{
moduleitem *m;
size_t lm, lv;
FILE *out = epicsGetStdout();
if (outfile)
{
out = fopen(outfile, "w");
if (out == NULL)
{
fprintf(stderr, "can't open %s: %s\n",
outfile, strerror(errno));
return -1;
}
}
for (m = loadedModules; m; m = m->next)
{
lm = strlen(m->content) + 1;
lv = strlen(m->content + lm) + 1;
fprintf(out, "%-*s%-20s %s\n",
(int)maxModuleNameLength, m->content,
m->content + lm, m->content + lm + lv);
}
if (fflush(out) < 0 && outfile)
{
fprintf(stderr, "can't write to %s: %s\n",
outfile, strerror(errno));
return -1;
}
if (outfile)
fclose(out);
return 0;
}
#define MISMATCH -1
#define EXACT 0
#define MATCH 1
#define TESTVERS 2
#define HIGHER 3
#define debug(...) \
if (requireDebug) \
printf(__VA_ARGS__)
static int compareDigit(int found, int requested, const char *name)
{
debug("require: compareDigit: found %d, requested %d for digit %s\n", found, requested, name);
if (found < requested)
{
debug("require: compareVersions: MISMATCH too low %s number\n", name);
return MISMATCH;
}
if (found > requested)
{
debug("require: compareVersions: HIGHER %s number\n", name);
return HIGHER;
}
return MATCH;
}
static int compareNumericVersion(semver_t *sv_found, semver_t *sv_request, int already_matched)
{
int match;
match = compareDigit(sv_found->major, sv_request->major, "major");
if (match != MATCH)
{
return match;
}
match = compareDigit(sv_found->minor, sv_request->minor, "minor");
if (match != MATCH)
{
return match;
}
match = compareDigit(sv_found->patch, sv_request->patch, "patch");
if (match != MATCH)
{
return match;
}
if (sv_request->build == -1)
{
if (already_matched)
{
debug("require: compareVersions: No build number requested. Returning HIGHER\n");
return HIGHER;
}
else
{
debug("require: compareVersions: No build number requested. Returning MATCH\n");
return MATCH;
}
}
return compareDigit(sv_found->build, sv_request->build, "build");
}
/*
* Returns if the version <found> is higher than <request>.
*/
static int compareVersions(const char *found, const char *request, int already_matched)
{
semver_t *sv_found, *sv_request;
int match;
debug("require: compareVersions(found=%s, request=%s)\n", found, request);
if (request == NULL || request[0] == 0)
{
debug("require: compareVersions: MATCH empty version requested\n");
return MATCH;
}
if (found == NULL || found[0] == 0)
{
debug("require: compareVersions: MISMATCH empty version found\n");
return MISMATCH;
}
sv_found = (semver_t *)calloc(1, sizeof(semver_t));
sv_request = (semver_t *)calloc(1, sizeof(semver_t));
parse_semver(found, sv_found);
parse_semver(request, sv_request);
// test version, look for exact.
if (strlen(sv_request->test_str) > 0)
{
if (strcmp(found, request) == 0)
{
debug("require: compareVersions: Test version requested and found, matches exactly\n");
match = EXACT;
}
else if (strlen(sv_found->test_str) > 0)
{
debug("require: compareVersions: Test versions requested and found, no match\n");
match = MISMATCH;
}
else
{
debug("require: compareVersions: found numeric version, higher than test\n");
match = HIGHER;
}
}
else if (strlen(sv_found->test_str) > 0)
{
debug("require: compareVersions: Numeric version requested, test version found\n");
match = MISMATCH;
}
else
{
match = compareNumericVersion(sv_found, sv_request, already_matched);
}
cleanup_semver(sv_found);
cleanup_semver(sv_request);
return match;
}
/* require (module)
Look if module is already loaded.
If module is already loaded check for version mismatch.
If module is not yet loaded load the library with ld,
load <module>.dbd with dbLoadDatabase (if file exists)
and call <module>_registerRecordDeviceDriver function.
If require is called from the iocsh before iocInit and fails,
it calls epicsExit to abort the application.
*/
/* wrapper to abort statup script */
static int require_priv(const char *module, const char *version, const char *args, const char *versionstr);
int require(const char *module, const char *version, const char *args)
{
int status;
char *versionstr;
static int firstTime = 1;
if (firstTime)
{
firstTime = 0;
set_require_env();
putenvprintf("T_A=%s", targetArch);
putenvprintf("EPICS_HOST_ARCH=%s", targetArch);
putenvprintf("EPICS_RELEASE=%s", epicsRelease);
putenvprintf("OS_CLASS=%s", osClass);
}
if (module == NULL)
{
printf("Usage: require \"<module>\" [, \"<version>\" | \"ifexists\"] [, \"<args>\"]\n");
printf("Loads " PREFIX "<module>" INFIX EXT " and <libname>.dbd\n");
printf("And calls <module>_registerRecordDeviceDriver\n");
printf("If available, runs startup script snippet (only before iocInit)\n");
return -1;
}
/* either order for version and args, either may be empty or NULL */
if (version && strchr(version, '='))
{
const char *v = version;
version = args;
args = v;
if (requireDebug)
printf("require: swap version and args\n");
}
if (version && version[0] == 0)
version = NULL;
if (version && strcmp(version, "none") == 0)
{
if (requireDebug)
printf("require: skip version=none\n");
return 0;
}
if (version)
{
/* needed for old style only: */
if (asprintf(&versionstr, "-%s", version) < 0)
return errno;
if (isdigit((unsigned char)version[0]) && version[strlen(version) - 1] == '+')
{
/*
user may give a minimal version (e.g. "1.2.4+")
load highest matching version (here "1.2") and check later
*/
char *p = strrchr(versionstr, '.');
if (p == NULL)
p = versionstr;
*p = 0;
}
}
else
versionstr = "";
if (requireDebug)
printf("require: versionstr = \"%s\"\n", versionstr);
status = require_priv(module, version, args, versionstr);
if (version)
free(versionstr);
if (status == 0)
return 0;
if (status != -1)
perror("require");
if (interruptAccept)
return status;
/* require failed in startup script before iocInit */
fprintf(stderr, "Aborting startup script\n");
epicsExit(1);
return status;
}
static off_t fileSize(const char *filename)
{
struct stat filestat;
if (stat(filename, &filestat) != 0)
{
if (requireDebug)
printf("require: %s does not exist\n", filename);
return -1;
}
switch (filestat.st_mode & S_IFMT)
{
case S_IFREG:
if (requireDebug)
printf("require: file %s exists, size %lld bytes\n",
filename, (unsigned long long)filestat.st_size);
return filestat.st_size;
case S_IFDIR:
if (requireDebug)
printf("require: directory %s exists\n",
filename);
return 0;
#ifdef S_IFBLK
case S_IFBLK:
if (requireDebug)
printf("require: %s is a block device\n",
filename);
return -1;
#endif
#ifdef S_IFCHR
case S_IFCHR:
if (requireDebug)
printf("require: %s is a character device\n",
filename);
return -1;
#endif
#ifdef S_IFIFO
case S_IFIFO:
if (requireDebug)
printf("require: %s is a FIFO/pipe\n",
filename);
return -1;
#endif
#ifdef S_IFSOCK
case S_IFSOCK:
if (requireDebug)
printf("require: %s is a socket\n",
filename);
return -1;
#endif
default:
if (requireDebug)
printf("require: %s is an unknown type of special file\n",
filename);
return -1;
}
}
#define fileExists(filename) (fileSize(filename) >= 0)
#define fileNotEmpty(filename) (fileSize(filename) > 0)
static int handleDependencies(const char *module, char *depfilename)
{
FILE *depfile;
char buffer[40];
char *end; /* end of string */
char *rmodule; /* required module */
char *rversion; /* required version */
if (requireDebug)
printf("require: parsing dependency file %s\n", depfilename);
depfile = fopen(depfilename, "r");
while (fgets(buffer, sizeof(buffer) - 1, depfile))
{
rmodule = buffer;
/* ignore leading spaces */
while (isspace((unsigned char)*rmodule))
rmodule++;
/* ignore empty lines and comment lines */
if (*rmodule == 0 || *rmodule == '#')
continue;
/* rmodule at start of module name */
rversion = rmodule;
/* find end of module name */
while (*rversion && !isspace((unsigned char)*rversion))
rversion++;
/* terminate module name */
*rversion++ = 0;
/* ignore spaces */
while (isspace((unsigned char)*rversion))
rversion++;
/* rversion at start of version */
if (*rversion)
{
end = rversion;
/* find end of version */
while (*end && !isspace((unsigned char)*end))
end++;
/* add + to numerial versions if not yet there */
/*
ESS would like to use the MATCH version only, not HIGHER.
In order to touch the PSI code mininaly, disable add + from VERSION from Dep file.
At the same time, ESS use the X.X.X instead of X.X.
Wednesday, May 2 00:12:18 CEST 2018, jhlee
*/
/*
if (*(end-1) != '+' && strspn(rversion, "0123456789.") == (size_t)(end-rversion)) *end++ = '+';
*/
/* terminate version */
*end = 0;
}
printf("Module %s depends on %s %s\n", module, rmodule, rversion);
if (require(rmodule, rversion, NULL) != 0)
{
fclose(depfile);
return -1;
}
}
fclose(depfile);
return 0;
}
static int
require_priv(const char *module,
const char *version,
const char *args,
const char *versionstr /* "-<version>" or "" (for old style only */
)
{
int status;
const char *loaded = NULL;
const char *found = NULL;
HMODULE libhandle;
int ifexists = 0;
const char *driverpath;
const char *dirname;
const char *end;
int releasediroffs;
int libdiroffs;
int extoffs;
char *founddir = NULL;
char *symbolname;
char filename[PATH_MAX];
int someVersionFound = 0;
int someArchFound = 0;
static char *globalTemplates = NULL;
if (requireDebug)
printf("require: module=\"%s\" version=\"%s\" args=\"%s\"\n", module, version, args);
#if defined __GNUC__ && __GNUC__ < 3
#define TRY_FILE(offs, args...) \
(snprintf(filename + offs, sizeof(filename) - offs, args) && fileExists(filename))
#define TRY_NONEMPTY_FILE(offs, args...) \
(snprintf(filename + offs, sizeof(filename) - offs, args) && fileNotEmpty(filename))
#else
#define TRY_FILE(offs, ...) \
(snprintf(filename + offs, sizeof(filename) - offs, __VA_ARGS__) && fileExists(filename))
#define TRY_NONEMPTY_FILE(offs, ...) \
(snprintf(filename + offs, sizeof(filename) - offs, __VA_ARGS__) && fileNotEmpty(filename))
#endif
#if defined(_WIN32)
/* enable %n in printf */
_set_printf_count_output(1);
#endif
driverpath = getenv("EPICS_DRIVER_PATH");
if (!globalTemplates)
{
char *t = getenv("TEMPLATES");
if (t)
globalTemplates = strdup(t);
}
if (driverpath == NULL)
driverpath = ".";
if (requireDebug)
printf("require: searchpath=%s\n", driverpath);
if (version && strcmp(version, "ifexists") == 0)
{
ifexists = 1;
version = NULL;
versionstr = "";
}
/* check already loaded verion */
loaded = getLibVersion(module);
if (loaded)
{
/* Library already loaded. Check Version. */
switch (compareVersions(loaded, version, FALSE))
{
case TESTVERS:
if (version)
printf("Warning: Module %s test version %s already loaded where %s was requested\n",
module, loaded, version);
case EXACT:
case MATCH:
printf("Module %s version %s already loaded\n", module, loaded);
break;
default:
printf("Conflict between requested %s version %s and already loaded version %s.\n",
module, version, loaded);
return -1;
}
dirname = getLibLocation(module);
if (dirname[0] == 0)
return 0;
if (requireDebug)
printf("require: library found in %s\n", dirname);
snprintf(filename, sizeof(filename), "%s%n", dirname, &releasediroffs);
putenvprintf("MODULE=%s", module);
pathAdd("SCRIPT_PATH", dirname);
}
else
{
if (requireDebug)
printf("require: no %s version loaded yet\n", module);
/* Search for module in driverpath */
for (dirname = driverpath; dirname != NULL; dirname = end)
{
/* get one directory from driverpath */
int dirlen;
int modulediroffs;
DIR_HANDLE dir;
DIR_ENTRY direntry;
end = strchr(dirname, OSI_PATH_LIST_SEPARATOR[0]);
if (end && end[1] == OSI_PATH_SEPARATOR[0] && end[2] == OSI_PATH_SEPARATOR[0]) /* "http://..." and friends */
end = strchr(end + 2, OSI_PATH_LIST_SEPARATOR[0]);
if (end)
dirlen = (int)(end++ - dirname);
else
dirlen = (int)strlen(dirname);
if (dirlen == 0)
continue; /* ignore empty driverpath elements */
if (requireDebug)
printf("require: trying %.*s\n", dirlen, dirname);
snprintf(filename, sizeof(filename), "%.*s" OSI_PATH_SEPARATOR "%s" OSI_PATH_SEPARATOR "%n",
dirlen, dirname, module, &modulediroffs);
dirlen++;
/* filename = "<dirname>/[dirlen]<module>/[modulediroffs]" */
/* Does the module directory exist? */
IF_OPEN_DIR(filename)
{
if (requireDebug)
printf("require: found directory %s\n", filename);
/* Now look for versions. */
START_DIR_LOOP
{
char *currentFilename = FILENAME(direntry);
SKIP_NON_DIR(direntry)
if (currentFilename[0] == '.')
continue; /* ignore hidden directories */
someVersionFound = 1;
/* Look for highest matching version. */
if (requireDebug)
printf("require: checking version %s against required %s\n",
currentFilename, version);
switch ((status = compareVersions(currentFilename, version, FALSE)))
{
case TESTVERS: /* test version found */
case EXACT: /* exact match found */
case MATCH: /* all given numbers match. */
{
someArchFound = 1;
if (requireDebug)
printf("require: %s %s may match %s\n",
module, currentFilename, version);
/* Check if it has our EPICS version and architecture. */
/* Even if it has no library, at least it has a dep file in the lib dir */
/* Step 1 : library file location */
/* filename = "<dirname>/[dirlen]<module>/[modulediroffs]" */
if (!TRY_FILE(modulediroffs, "%s" OSI_PATH_SEPARATOR LIBDIR "%s" OSI_PATH_SEPARATOR,
currentFilename, targetArch))
/* filename = "<dirname>/[dirlen]<module>/[modulediroffs]<version>/lib/<targetArch>/" */
{
if (requireDebug)
printf("require: %s %s has no support for %s %s\n",
module, currentFilename, epicsRelease, targetArch);
continue;
}
if (status == EXACT)
{
if (requireDebug)
printf("require: %s %s matches %s exactly\n",
module, currentFilename, version);
/* We are done. */
end = NULL;
break;
}
/* Is it higher than the one we found before? */
if (found && requireDebug)
printf("require: %s %s support for %s %s found, compare against previously found %s\n",
module, currentFilename, epicsRelease, targetArch, found);
if (!found || compareVersions(currentFilename, found, TRUE) == HIGHER)
{
if (requireDebug)
printf("require: %s %s looks promising\n", module, currentFilename);
break;
}
if (requireDebug)
printf("require: version %s is lower than %s \n", currentFilename, found);
continue;
}
default:
{
if (requireDebug)
printf("require: %s %s does not match %s\n",
module, currentFilename, version);
continue;
}
}
/* we have found something (EXACT or MATCH) */
free(founddir);
/* filename = "<dirname>/[dirlen]<module>/[modulediroffs]..." */
if (asprintf(&founddir, "%.*s%s", modulediroffs, filename, currentFilename) < 0)
return errno;
/* founddir = "<dirname>/[dirlen]<module>/[modulediroffs]<version>" */
found = founddir + modulediroffs; /* version part in the path */
if (status == EXACT)
break;
}
END_DIR_LOOP
}
else
{
/* filename = "<dirname>/[dirlen]<module>/" */
if (requireDebug)
printf("require: no %s directory\n", filename);
/* try local/old style module only if no new style candidate has been found */
if (!found)
{
/* look for dep file */
releasediroffs = libdiroffs = dirlen;
if (TRY_FILE(dirlen, "%s%s.dep", module, versionstr))
/* filename = "<dirname>/[dirlen][releasediroffs][libdiroffs]<module>(-<version>)?.dep" */
{
if (requireDebug)
printf("require: found old style %s\n", filename);
printf("Module %s%s found in %.*s\n", module,
versionstr, dirlen, filename);
goto checkdep;
}
/* look for library file */
if (TRY_FILE(dirlen, PREFIX "%s" INFIX "%s%n" EXT, module, versionstr, &extoffs)
/* filename = "<dirname>/[dirlen][releasediroffs][libdiroffs]PREFIX<module>INFIX(-<version>)?[extoffs]EXT" */
)
{
if (requireDebug)
printf("require: found old style %s\n", filename);
printf("Module %s%s found in %.*s\n", module,
versionstr, dirlen, filename);
goto loadlib;
}
}
}
/* filename = "<dirname>/[dirlen]..." */
if (!found && requireDebug)
printf("require: no matching version in %.*s\n", dirlen, filename);
}
if (!found)
{
if (someArchFound)
fprintf(stderr, "Module %s%s%s not available for %s\n(but maybe for other EPICS versions or architectures)\n",
module, version ? " version " : "", version ? version : "", targetArch);
else if (someVersionFound)
fprintf(stderr, "Module %s%s%s not available (but other versions are available)\n",
module, version ? " version " : "", version ? version : "");
else
fprintf(stderr, "Module %s%s%s not available\n",
module, version ? " version " : "", version ? version : "");
return ifexists ? 0 : -1;
}
versionstr = "";
/* founddir = "<dirname>/[dirlen]<module>/<version>" */
printf("Module %s version %s found in %s" OSI_PATH_SEPARATOR "\n", module, found, founddir);
/* Step 2 : Looking for Dep file */
if (requireDebug)
printf("require: looking for dependency file\n");
if (!TRY_FILE(0, "%s" OSI_PATH_SEPARATOR "%n" LIBDIR "%s" OSI_PATH_SEPARATOR "%n%s.dep",
founddir, &releasediroffs, targetArch, &libdiroffs, module))
/* filename = "<dirname>/[dirlen]<module>/<version>/R<epicsRelease>/[releasediroffs]/lib/<targetArch>/[libdiroffs]/module.dep" */
{
fprintf(stderr, "Dependency file %s not found\n", filename);
}
else
{
checkdep:
/* filename = "<dirname>/[dirlen]<module>/<version>/R<epicsRelease>/[releasediroffs]/lib/<targetArch>/[libdiroffs]/module.dep" */
/* or (old) "<dirname>/[dirlen]][releasediroffs][libdiroffs]<module>(-<version>)?.dep" */
if (handleDependencies(module, filename) == -1)
return -1;
}
if (requireDebug)
printf("require: looking for library file\n");
if (!(TRY_FILE(libdiroffs, PREFIX "%s" INFIX "%s%n" EXT, module, versionstr, &extoffs)))
/* filename = "<dirname>/[dirlen]<module>/<version>/R<epicsRelease>/[releasediroffs]/lib/<targetArch>/[libdiroffs]/PREFIX<module>INFIX[extoffs](EXT)?" */
/* or (old) "<dirname>/[dirlen][releasediroffs][libdiroffs]PREFIX<module>INFIX(-<version>)?[extoffs](EXT)?" */
{
printf("Module %s has no library\n", module);
}
else
{
loadlib:
/* filename = "<dirname>/[dirlen]<module>/<version>/R<epicsRelease>/[releasediroffs]/lib/<targetArch>/[libdiroffs]/PREFIX<module>INFIX[extoffs]EXT" */
/* or (old) "<dirname>/[dirlen][releasediroffs][libdiroffs]PREFIX<module>INFIX(-<version>)?[extoffs]EXT" */
printf("Loading library %s\n", filename);
if ((libhandle = loadlib(filename)) == NULL)
return -1;
/* now check what version we really got (with compiled-in version number) */
if (asprintf(&symbolname, "_%sLibRelease", module) < 0)
return errno;
found = (const char *)getAddress(libhandle, symbolname);
free(symbolname);
printf("Loaded %s version %s\n", module, found);
/* check what we got */
if (requireDebug)
printf("require: compare requested version %s with loaded version %s\n", version, found);
if (compareVersions(found, version, FALSE) == MISMATCH)
{
fprintf(stderr, "Requested %s version %s not available, found only %s.\n",
module, version, found);
return -1;
}
/* load dbd file */
if (TRY_NONEMPTY_FILE(releasediroffs, "dbd" OSI_PATH_SEPARATOR "%s%s.dbd", module, versionstr) ||
TRY_NONEMPTY_FILE(releasediroffs, "%s%s.dbd", module, versionstr) ||
TRY_NONEMPTY_FILE(releasediroffs, ".." OSI_PATH_SEPARATOR "dbd" OSI_PATH_SEPARATOR "%s%s.dbd", module, versionstr) ||
TRY_NONEMPTY_FILE(releasediroffs, ".." OSI_PATH_SEPARATOR "%s%s.dbd", module, versionstr) ||
TRY_NONEMPTY_FILE(releasediroffs, ".." OSI_PATH_SEPARATOR ".." OSI_PATH_SEPARATOR "dbd" OSI_PATH_SEPARATOR "%s.dbd", module)) /* org EPICSbase */
{
printf("Loading dbd file %s\n", filename);
if (dbLoadDatabase(filename, NULL, NULL) != 0)
{
fprintf(stderr, "Error loading %s\n", filename);
return -1;
}
/* when dbd is loaded call register function */
if (asprintf(&symbolname, "%s_registerRecordDeviceDriver", module) < 0)
return errno;
printf("Calling function %s\n", symbolname);
iocshCmd(symbolname);
free(symbolname);
}
else
{
/* no dbd file, but that might be OK */
printf("%s has no dbd file\n", module);
}
}
/* register module with path */
filename[releasediroffs] = 0;
registerModule(module, found, filename);
}
status = 0;
if (requireDebug)
printf("require: looking for template directory\n");
/* filename = "<dirname>/[dirlen]<module>/<version>/R<epicsRelease>/[releasediroffs]..." */
if (!((TRY_FILE(releasediroffs, TEMPLATEDIR) ||
TRY_FILE(releasediroffs, ".." OSI_PATH_SEPARATOR TEMPLATEDIR)) &&
setupDbPath(module, filename) == 0))
{
/* if no template directory found, restore TEMPLATES to initial value */
char *t;
t = getenv("TEMPLATES");
if (globalTemplates && (!t || strcmp(globalTemplates, t) != 0))
putenvprintf("TEMPLATES=%s", globalTemplates);
}
if (loaded && args == NULL)
return 0; /* no need to execute startup script twice if not with new arguments */
return status;
}
static const iocshFuncDef requireDef = {
"require", 3, (const iocshArg *[]){
&(iocshArg){"module", iocshArgString},
&(iocshArg){"[version]", iocshArgString},
&(iocshArg){"[substitutions]", iocshArgString},
}};
static void requireFunc(const iocshArgBuf *args)
{
require(args[0].sval, args[1].sval, args[2].sval);
}
static const iocshFuncDef libversionShowDef = {
"libversionShow", 1, (const iocshArg *[]){
&(iocshArg){"outputfile", iocshArgString},
}};
static void libversionShowFunc(const iocshArgBuf *args)
{
libversionShow(args[0].sval);
}
static const iocshFuncDef ldDef = {
"ld", 1, (const iocshArg *[]){
&(iocshArg){"library", iocshArgString},
}};
static void ldFunc(const iocshArgBuf *args)
{
loadlib(args[0].sval);
}
static const iocshFuncDef pathAddDef = {
"pathAdd", 2, (const iocshArg *[]){
&(iocshArg){"ENV_VARIABLE", iocshArgString},
&(iocshArg){"directory", iocshArgString},
}};
static void pathAddFunc(const iocshArgBuf *args)
{
pathAdd(args[0].sval, args[1].sval);
}
static void requireRegister(void)
{
static int firstTime = 1;
if (firstTime)
{
firstTime = 0;
iocshRegister(&requireDef, requireFunc);
iocshRegister(&libversionShowDef, libversionShowFunc);
iocshRegister(&ldDef, ldFunc);
iocshRegister(&pathAddDef, pathAddFunc);
registerExternalModules();
}
}
epicsExportRegistrar(requireRegister);
epicsExportAddress(int, requireDebug);
registrar(requireRegister)
variable(requireDebug,int)
#ifndef require_h
#define require_h
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __GNUC__
#define __attribute__(dummy)
#endif
int require(const char* libname, const char* version, const char* args);
size_t foreachLoadedLib(size_t (*func)(const char* name, const char* version, const char* path, void* arg), void* arg);
const char* getLibVersion(const char* libname);
const char* getLibLocation(const char* libname);
int libversionShow(const char* outfile);
int runScript(const char* filename, const char* args);
int putenvprintf(const char* format, ...) __attribute__((__format__(__printf__,1,2)));
void pathAdd(const char* varname, const char* dirname);
#ifdef __cplusplus
}
#endif
#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <errno.h>
#include <macLib.h>
#include <dbAccess.h>
#include <initHooks.h>
#include <epicsVersion.h>
#define EPICSVER EPICS_VERSION*10000+EPICS_REVISION*100+EPICS_MODIFICATION
#if defined (_WIN32)
#include "asprintf.h"
#endif
#include <osiFileName.h>
#include <iocsh.h>
epicsShareFunc int epicsShareAPI iocshCmd(const char *cmd);
#include <epicsExport.h>
#define IS_ABS_PATH(filename) (filename[0] == OSI_PATH_SEPARATOR[0]) /* may be different for other OS ? */
#include "expr.h"
#include "require.h"
#define SAVEENV(var) do { old_##var = getenv(#var); if (old_##var) old_##var=strdup(old_##var); } while(0)
#define RESTOREENV(var) do { if(old_##var) { putenvprintf("%s=%s", #var, old_##var); free(old_##var); }} while(0)
int runScriptDebug=0;
int runScript(const char* filename, const char* args)
{
MAC_HANDLE *mac = NULL;
FILE* file = NULL;
char* line_raw = NULL;
char* line_exp = NULL;
long line_raw_size = 256;
long line_exp_size = line_raw_size;
long len;
char** pairs;
int status = 0;
char* old_MODULE = NULL;
char* old_MODULE_DIR = NULL;
if (!filename)
{
fprintf(stderr, "Usage: runScript filename [macro=value,...]\n");
return -1;
}
if (macCreateHandle(&mac,(const char*[]){ "", "environ", NULL, NULL }) != 0) goto error;
macSuppressWarning(mac, 1);
if ((line_exp = malloc(line_exp_size)) == NULL) goto error;
if ((line_raw = malloc(line_raw_size)) == NULL) goto error;
/* add args to macro definitions */
if (args)
{
if (runScriptDebug)
printf("runScript: macParseDefns \"%s\"\n", args);
macParseDefns(mac, (char*)args, &pairs);
macInstallMacros(mac, pairs);
free(pairs);
}
if (IS_ABS_PATH(filename))
{
file = fopen(filename, "r");
}
else
{
const char* dirname;
const char* end;
char* fullname;
const char* path = getenv("SCRIPT_PATH");
int dirlen;
for (dirname = path; dirname != NULL; dirname = end)
{
end = strchr(dirname, OSI_PATH_LIST_SEPARATOR[0]);
if (end && end[1] == OSI_PATH_SEPARATOR[0] && end[2] == OSI_PATH_SEPARATOR[0]) /* "http://..." and friends */
end = strchr(end+2, OSI_PATH_LIST_SEPARATOR[0]);
if (end) dirlen = (int)(end++ - dirname);
else dirlen = (int)strlen(dirname);
if (dirlen == 0) continue; /* ignore empty path elements */
if (dirname[dirlen-1] == OSI_PATH_SEPARATOR[0]) dirlen--;
asprintf(&fullname, "%.*s" OSI_PATH_SEPARATOR "%s",
dirlen, dirname, filename);
if (runScriptDebug)
printf("runScript: trying %s\n", fullname);
file = fopen(fullname, "r");
if (!file && (errno & 0xffff) != ENOENT) perror(fullname);
free(fullname);
if (file) break;
}
}
if (file == NULL) { perror(filename); return errno; }
/* save some environments variables */
SAVEENV(MODULE);
SAVEENV(MODULE_DIR);
/* execute script line by line after expanding macros with arguments or environment */
while (fgets(line_raw, line_raw_size, file))
{
char* p, *x;
/* check if we have a line longer than the buffer size */
while (line_raw[(len = (long)strlen(line_raw))-1] != '\n' && !feof(file))
{
if (runScriptDebug)
printf("runScript partial line: \"%s\"\n", line_raw);
if ((line_raw = realloc(line_raw, line_raw_size *= 2)) == NULL) goto error;
if (fgets(line_raw + len, line_raw_size - len, file) == NULL) break;
}
while (len > 0 && isspace((unsigned char)line_raw[len-1])) line_raw[--len] = 0; /* get rid of '\n' and friends */
if (runScriptDebug)
printf("runScript raw line (%ld chars): '%s'\n", len, line_raw);
/* expand and check the buffer size (different epics versions write different may number of bytes)*/
while ((len = labs(macExpandString(mac, line_raw, line_exp, line_exp_size-1))) >= line_exp_size-2)
{
if (runScriptDebug)
printf("runScript: grow expand buffer: len=%ld size=%ld\n", len, line_exp_size);
free(line_exp);
if ((line_exp = malloc(line_exp_size *= 2)) == NULL) goto error;
}
if (runScriptDebug)
printf("runScript expanded line (%ld chars): '%s'\n", len, line_exp);
printf("%s\n", line_exp);
p = line_exp;
while (isspace((unsigned char)*p)) p++;
if (*p == 0 || *p == '#') continue;
/* find local variable assignments */
if ((x = strpbrk(p, "=(, \t\n\r")) != NULL && *x=='=')
{
*x++ = 0;
replaceExpressions(x, line_raw, line_raw_size);
if (runScriptDebug)
printf("runScript: assign %s=%s\n", p, line_raw);
macPutValue(mac, p, line_raw);
continue;
}
if (runScriptDebug)
printf("runScript: iocshCmd: '%s'\n", line_exp);
status = iocshCmd(line_exp);
if (status != 0) break;
}
goto end;
error:
if (errno)
{
status = errno;
perror("runScript");
}
end:
free(line_raw);
free(line_exp);
if (mac) macDeleteHandle(mac);
if (file) fclose(file);
/* restore environment */
RESTOREENV(MODULE);
RESTOREENV(MODULE_DIR);
return status;
}
struct cmditem
{
struct cmditem* next;
int type;
union {
char* a[12];
char cmd[256];
} x;
} *cmdlist, **cmdlast=&cmdlist;
void afterInitHook(initHookState state)
{
struct cmditem *item;
if (state !=
#ifdef INCinitHooksh
/* old: without iocPause etc */
initHookAfterInterruptAccept
#else
/* new: with iocPause etc */
initHookAfterIocRunning
#endif
) return;
for (item = cmdlist; item != NULL; item = item->next)
{
if (item->type == 1)
{
printf("%s\n", item->x.cmd);
iocshCmd(item->x.cmd);
}
else
((void (*)())item->x.a[0])(item->x.a[1], item->x.a[2], item->x.a[3], item->x.a[4], item->x.a[5],
item->x.a[6], item->x.a[7], item->x.a[8], item->x.a[9], item->x.a[10], item->x.a[11]);
}
}
static int first_time = 1;
static struct cmditem *newItem(char* cmd, int type)
{
struct cmditem *item;
if (!cmd)
{
fprintf(stderr, "usage: afterInit command, args...\n");
return NULL;
}
if (interruptAccept)
{
fprintf(stderr, "afterInit can only be used before iocInit\n");
return NULL;
}
if (first_time)
{
first_time = 0;
initHookRegister(afterInitHook);
}
item = malloc(sizeof(struct cmditem));
if (item == NULL)
{
perror("afterInit");
return NULL;
}
item->type = type;
item->next = NULL;
*cmdlast = item;
cmdlast = &item->next;
return item;
}
int afterInit(char* cmd, char* a1, char* a2, char* a3, char* a4, char* a5, char* a6, char* a7, char* a8, char* a9, char* a10, char* a11)
{
struct cmditem *item = newItem(cmd, 0);
if (!item) return -1;
item->x.a[0] = cmd;
item->x.a[1] = a1;
item->x.a[2] = a2;
item->x.a[3] = a3;
item->x.a[4] = a4;
item->x.a[5] = a5;
item->x.a[6] = a6;
item->x.a[7] = a7;
item->x.a[8] = a8;
item->x.a[9] = a9;
item->x.a[10] = a10;
item->x.a[11] = a11;
return 0;
}
epicsExportAddress(int, runScriptDebug);
epicsExportAddress(int, exprDebug);
static const iocshFuncDef runScriptDef = {
"runScript", 2, (const iocshArg *[]) {
&(iocshArg) { "filename", iocshArgString },
&(iocshArg) { "substitutions", iocshArgString },
}};
static void runScriptFunc(const iocshArgBuf *args)
{
runScript(args[0].sval, args[1].sval);
}
static const iocshFuncDef afterInitDef = {
"afterInit", 1, (const iocshArg *[]) {
&(iocshArg) { "commandline", iocshArgArgv },
}};
static void afterInitFunc(const iocshArgBuf *args)
{
int i, n;
struct cmditem *item = newItem(args[0].aval.av[1], 1);
if (!item) return;
n = sprintf(item->x.cmd, "%.255s", args[0].aval.av[1]);
for (i = 2; i < args[0].aval.ac; i++)
{
if (strpbrk(args[0].aval.av[i], " ,\"\\"))
n += sprintf(item->x.cmd+n, " '%.*s'", 255-3-n, args[0].aval.av[i]);
else
n += sprintf(item->x.cmd+n, " %.*s", 255-1-n, args[0].aval.av[i]);
}
}
static void runScriptRegister(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
iocshRegister (&runScriptDef, runScriptFunc);
iocshRegister (&afterInitDef, afterInitFunc);
}
}
epicsExportRegistrar(runScriptRegister);
registrar(runScriptRegister)
variable(runScriptDebug,int)
variable(exprDebug,int)
#include <string.h>
#include <stdlib.h>
char *strdup(const char *s)
{
char *d = malloc(strlen(s)+1);
if (d) strcpy(d,s);
return d;
}
char *strndup(const char *s, size_t n)
{
size_t l;
char *d;
l = strlen(s);
if (n > l) n = l;
d = malloc(n+1);
strncpy(d,s,l);
d[n] = 0;
return d;
}
#ifndef strdup_h
#define strdup_h
#ifdef __cplusplus
extern "C" {
#endif
char *strdup(const char *s);
char *strndup(const char *s, size_t n);
#ifdef __cplusplus
}
#endif
#endif
#include <regex.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "version.h"
void cleanup_semver(semver_t *s)
{
free(s->version_str);
free(s);
}
static void init_semver(const char *version, semver_t *s)
{
s->version_str = calloc(strlen(version) + 1, sizeof(char));
strcpy(s->version_str, version);
s->test_str = "";
s->major = 0;
s->minor = 0;
s->patch = 0;
s->build = -1;
}
static void fetch_part(char *version_str, const regmatch_t *groups, unsigned int ix, int *part)
{
char tmp;
tmp = version_str[groups[ix].rm_eo];
version_str[groups[ix].rm_eo] = 0;
*part = atoi(version_str + groups[ix].rm_so);
version_str[groups[ix].rm_eo] = tmp;
}
int parse_semver(const char *version, semver_t *s)
{
static const char* version_regex = "^(([0-9]+)\\.([0-9]+)\\.([0-9]+)(\\+([0-9]+))?)?(.*)$";
static const unsigned int max_regex_groups = 7 + 1;
static const unsigned int major_ix = 2;
static const unsigned int minor_ix = 3;
static const unsigned int patch_ix = 4;
static const unsigned int build_ix = 6;
static const unsigned int test_ix = 7;
regex_t compiled;
regmatch_t groups[max_regex_groups];
init_semver(version, s);
if (s->version_str == NULL || s->version_str[0] == 0)
{
return 1;
}
if (regcomp(&compiled, version_regex, REG_EXTENDED))
{
return 1;
}
if (regexec(&compiled, s->version_str, max_regex_groups, groups, 0) == 0)
{
fetch_part(s->version_str, groups, major_ix, &s->major);
fetch_part(s->version_str, groups, minor_ix, &s->minor);
fetch_part(s->version_str, groups, patch_ix, &s->patch);
if (groups[build_ix].rm_so != (regoff_t)-1)
{
fetch_part(s->version_str, groups, build_ix, &s->build);
}
s->test_str = s->version_str + groups[test_ix].rm_so;
}
return 0;
}