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 745 additions and 2830 deletions
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
#include "iocsh.h"
#include "epicsExport.h"
#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;
}
#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);
/*************************************************************************\
* 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 <stdio.h>
#include <stdlib.h>
#include <string.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
#include "module.h"
#include <dbAccess.h>
#include <errlog.h>
#ifdef __MACH__
#include <mach/error.h>
#else
#include <error.h>
#endif
#include <limits.h>
#include <osiFileName.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#define MAX_MODULE_SIZE 256
unsigned long int bufferSize = 0;
const char *getLibVersion(struct linkedList *linkedlist, const char *libname) {
struct module *m = NULL;
for (m = linkedlist->head; m; m = m->next) {
if (strcmp(m->name, libname) == 0) {
return m->version;
}
}
return NULL;
}
const char *getLibLocation(struct linkedList *linkedlist, const char *libname) {
struct module *m = NULL;
for (m = linkedlist->head; m; m = m->next) {
if (strcmp(m->name, libname) == 0) {
return m->path;
}
}
return NULL;
}
int isModuleLoaded(struct linkedList *linkedlist, const char *libname) {
struct module *m = NULL;
for (m = linkedlist->head; m; m = m->next) {
if (strcmp(m->name, libname) == 0) return TRUE;
}
return FALSE;
}
int registerModule(struct linkedList *linkedlist, const char *moduleName,
const char *version, const char *location) {
char *absLocation = NULL;
char *absLocationRequire = NULL;
char *argstring = NULL;
const char *mylocation = NULL;
/* require should be called only before iocInit. */
if (interruptAccept) return 0;
debug("require: registerModule(%s,%s,%s)\n", moduleName, version, location);
if (!moduleName) return -1;
if (!version) version = "";
if (location) {
absLocation = realpathSeparator(location);
}
struct module *module = NULL;
if (!(module = (struct module *)calloc(sizeof(struct module), 1))) {
goto out_of_memory;
}
/* Check if string is well formated, there is a \0 in the next MAX_MODULE_SIZE
bytes. */
int nameSize = strnlen(moduleName, MAX_MODULE_SIZE) + 1;
if (nameSize > MAX_MODULE_SIZE) return -1;
if (!(module->name = calloc(nameSize, sizeof(char)))) {
goto out_of_memory;
}
strcpy(module->name, moduleName);
int versionSize = strnlen(version, MAX_MODULE_SIZE) + 1;
if (versionSize > MAX_MODULE_SIZE) return -1;
if (!(module->version = calloc(versionSize, sizeof(char)))) {
goto out_of_memory;
}
strcpy(module->version, version);
if (!(module->path =
calloc(strnlen(absLocation, PATH_MAX) + 1, sizeof(char)))) {
goto out_of_memory;
}
strcpy(module->path, absLocation ? absLocation : "");
free(absLocation);
/* This bufferSize is used to calculate the ModuleVersions buffer size. It
* will be updated every time we call dbLoadRecords at the end of this
* function. The size here will be calculated based on the string that is
* being written in fillModuleListRecord. So the magic number here is related
* to that string format.*/
bufferSize += nameSize + versionSize + 2;
if (linkedlist->size == 0) {
linkedlist->head = module;
} else {
linkedlist->tail->next = module;
}
linkedlist->tail = module;
linkedlist->size++;
putenvprintf("MODULE=%s", module->name);
putenvprintf("%s_VERSION=%s", module->name, module->version);
if (location) {
putenvprintf("%s_DIR=%s", module->name, module->path);
pathAdd("SCRIPT_PATH", module->path);
}
/* create a record with the version string */
mylocation = getenv("require_DIR");
if (mylocation == NULL) return 0;
if (asprintf(&absLocationRequire,
"%s" OSI_PATH_SEPARATOR "db" OSI_PATH_SEPARATOR
"moduleversion.template",
mylocation) < 0)
return 0;
/*
Require DB has the following four PVs:
- $(REQUIRE_IOC):$(MODULE)Version
- $(REQUIRE_IOC):ModuleVersions
- $(REQUIRE_IOC):Versions
- $(REQUIRE_IOC):Modules
We reserved 30 chars for :$(MODULE)Version, 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=%u, BUFFER_SIZE=%lu",
getenv("REQUIRE_IOC"), module->name, module->version,
linkedlist->size, bufferSize) < 0) {
errlogPrintf("Error asprintf failed\n");
return 0;
}
printf("Loading module info records for %s\n", module->name);
dbLoadRecords(absLocationRequire, argstring);
free(argstring);
free(absLocationRequire);
return 0;
out_of_memory:
errlogPrintf("require: out of memory\n");
return -1;
}
#ifndef __MODULE_H__
#define __MODULE_H__
struct module {
struct module *next;
char *name;
char *version;
char *path;
};
struct linkedList {
struct module *head;
struct module *tail;
unsigned int size;
};
const char *getLibVersion(struct linkedList *linkedlist, const char *libname);
const char *getLibLocation(struct linkedList *linkedlist, const char *libname);
int isModuleLoaded(struct linkedList *linkedlist, const char *libname);
int registerModule(struct linkedList *linkedlist, const char *module,
const char *version, const char *location);
#endif /*__MODULE_H__*/
This diff is collapsed.
#ifndef require_h
#define require_h
/* Copyright (C) 2020 Dirk Zimoch */
/* Copyright (C) 2020-2022 European Spallation Source, ERIC */
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#endif // __cplusplus
#ifndef __GNUC__
#define __attribute__(dummy)
#endif
#endif // __GNUC__
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 require(const char *libname, const char *version);
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
#endif // __cplusplus
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <ctype.h>
#include <dbAccess.h>
#include <epicsVersion.h>
#include <errno.h>
#include <initHooks.h>
#include <macLib.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define EPICSVER \
EPICS_VERSION * 10000 + EPICS_REVISION * 100 + EPICS_MODIFICATION
#if defined(_WIN32)
#include "asprintf.h"
#endif
#include <iocsh.h>
#include <osiFileName.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 <stdlib.h>
#include <string.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
/* Copyright (C) 2020-2022 European Spallation Source, ERIC */
#include "version.h"
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
......@@ -9,15 +12,15 @@ void cleanup_semver(semver_t *s) {
free(s);
}
static void init_semver(const char *version, semver_t *s) {
static semver_t *init_semver(const char *version) {
semver_t *s = (semver_t *)calloc(1, sizeof(semver_t));
s->version_str = calloc(strlen(version) + 1, sizeof(char));
strcpy(s->version_str, version);
s->test_str = "";
s->revision = -1;
s->major = 0;
s->minor = 0;
s->patch = 0;
s->build = -1;
return s;
}
static void fetch_part(char *version_str, const regmatch_t *groups,
......@@ -30,37 +33,41 @@ static void fetch_part(char *version_str, const regmatch_t *groups,
version_str[groups[ix].rm_eo] = tmp;
}
int parse_semver(const char *version, semver_t *s) {
semver_t *parse_semver(const char *version) {
static const char *version_regex =
"^(([0-9]+)\\.([0-9]+)\\.([0-9]+)(\\+([0-9]+))?)?(.*)$";
"^(([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;
static const unsigned int test_label_ix = 5;
static const unsigned int revision_ix = 7;
semver_t *s = init_semver(version);
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;
return NULL;
}
if (regcomp(&compiled, version_regex, REG_EXTENDED)) {
return 1;
regfree(&compiled);
return NULL;
}
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);
if (groups[test_label_ix].rm_so != groups[test_label_ix].rm_eo) {
s->test_str = s->version_str;
}
if (groups[revision_ix].rm_so != (regoff_t)-1) {
fetch_part(s->version_str, groups, revision_ix, &s->revision);
s->version_str[groups[revision_ix].rm_so - 1] = 0;
}
s->test_str = s->version_str + groups[test_ix].rm_so;
}
return 0;
regfree(&compiled);
return s;
}
/* Copyright (C) 2020-2022 European Spallation Source, ERIC */
#pragma once
typedef struct semver_t {
char *version_str;
int major;
int minor;
int patch;
int build; // can be negative; implies that build has not been specified
int revision; // can be negative; implies that revision has not been
// specified
char *test_str;
} semver_t;
void cleanup_semver(semver_t *s);
int parse_semver(const char *version, semver_t *s);
semver_t *parse_semver(const char *version);
#var exprDebug 1
x=3, !3, !!3, -3, --3, ~3, ~~3, 3?, 3??
# $(x) should be: 3, 0, 1, -3, 3, -4, 3, 1, 1
x=x, !x, !!x, -x, --x, ~x, ~~x, x?, x??
# $(x) should be: x, !x, !!x, -x, --x, ~x, ~~x, x?, x??
x=10+3, 10-3, 10*3, 10/3, 10%3, 10**3
# $(x) should be: 13, 7, 30, 3, 1, 1000
x=7**-2 7**-1 7**0 7**1 7**2 7**3 7**4 7**5 7**6
# $(x) should be: 0 0 1 7 49 343 2401 16807 117649
x=%x -10<<2, %x -10>>2, %x -10>>>2
# $(x) should be: ffffffffffffffd8, fffffffffffffffd, 3ffffffffffffffd
x=%x 0xaa & 0x0f, %x 0xaa | 0x0f, %x 0xaa ^ 0x0f
# $(x) should be: a, af, a5
x=10<20 10<=20 10==20 10>=20 10>20 10<=>20 10<?20 10>?20 10?<20 10?>20
# $(x) should be: 1 1 0 0 0 -1 10 20 1 0
x=20<20 20<=20 20==20 20>=20 20>20 20<=>20 20<?20 20>?20 20?<20 20?>20
# $(x) should be: 0 1 1 1 0 0 20 20 1 0
x=20<10 20<=10 20==10 20>=10 20>10 20<=>10 20<?10 20>?10 20?<10 20?>10
# $(x) should be: 0 0 0 1 1 1 10 20 1 0
x= (0|0)(0|1)(1|0)(1|1) (0&0)(0&1)(1&0)(1&1) (0^0)(0^1)(1^0)(1^1)
# $(x) should be: 0111 0001 0110
x=1*2*3+4*5*6-7-8
# $(x) should be: 111
x=1*2*3+4*5*6-7*8
# $(x) should be: 70
x=-3**2+1, -(3**2)+1, (-3)**2+1, -3**-2+1, (1+2), (1+2, 1+2)
# $(x) should be: 10, -8, 10, 1, 3, (1+2, 3)
x=%#X 0x5555&0xfff0|0xaf<<8^0xa00a0>>4
# $(x) should be: 0X5F5A
x=7+4*2|32
# $(x) should be: 47
x=3<5 ? 7+4*2|32 : 9 & 10
# $(x) should be: 47
x=3>5 ? 7+4*2|32 : 9 & 10
# $(x) should be: 8
x=7 ?: -1
# $(x) should be: 7
x=0 ?: -1
# $(x) should be: -1
x=7 ? -1
# $(x) should be: -1
x=0 ? -1
# $(x) should be: 0
x=1+0?7?1:2:3?4:5
# $(x) should be: 1
x=1+0?0?1:2:3?4:5
# $(x) should be: 2
x=1-1?0?1:2:3?4:5
# $(x) should be: 4
x=1-1?0?1:2:3-3?4:5
# $(x) should be: 5
x=0?; 1?; 2?; -4?; 3?+1
# $(x) should be: 0; 1; 1; 1; 4
x=a030b a(030)b a( 030 )b "a"030"b" a(%x 030)b
# $(x) should be: a030b a24b a24b a24b a18b
x=030b (030)b ( 030 )b 030"b" %x 030'b'
# $(x) should be: 030b 24b 24b 24b 18b
x=-0bla -0.1
# $(x) should be: -0bla -0.1
x=-2**2 0-2**2 0+-2**2 0--2**2
# $(x) should be: 4 -4 4 -4
x=
y=$(x)
# <$(x)><$(y)> should be: <><>
x=string0
y=$(x)
# $(x) $(y)
x="string1"
y=$(x)
# $(x) $(y)
x="'string2'"
y=$(x)
# $(x) $(y)
x='"string3"'
y=$(x)
# $(x) $(y)
x=\"string4\"
y=$(x)
# $(x) $(y)
x=\'string5\'
y=$(x)
# $(x) $(y)
x="Hello World"[2:8]
# $(x) should be: llo Wo
x=Hello World[2:8]
# "$(x)" should be: "Hello World[2:8]"
x="Hello World"[2,3]
# $(x) should be: llo
x="Hello World"[1]
# $(x) should be: e
x="Hello World"[-1]
# $(x) should be: d
x="Hello World"[-2:]
# $(x) should be: ld
x="Hello World"[-2,6]
# $(x) should be: ld
x="Hello World"[1:1000]
# $(x) should be: ello World
x="Hello World"[1:-1000]
# "$(x)" should be: ""
x="Hello World"[:]
# "$(x)" should be: "Hello World"
x="Hello World"[1?6:0:]
# "$(x)" should be: "World"
x="Hello World"[1000]
# "$(x)" should be: ""
x="Hello World"[-1000]
# "$(x)" should be: ""
x="Hello World"[-1000,1005]
# "$(x)" should be: "World"
x="Hello World"[-1000:5]
# "$(x)" should be: "World"
x="Hello World"[7:4]
# "$(x)" should be: ""
x="Hello World"[1?3+4:0:7-8]
# "$(x)" should be: "orl"
x="Hello World"[7,-1]
# "$(x)" should be: ""
x='\"\\x'[2]
# $(x) should be: x
x=#"Hello World"
# $(x) should be: 11
x=#"Hello World"[2:8]
# $(x) should be: 6
x=#Hello World
# $(x) should be: #Hello World
x=1 ? "true" : "false"
# $(x) should be: true
iocsh.bash
====
## E3 Unique Variables
```
E3_CMD_TOP : the absolute path where a startup script (cmd file) is
E3_IOCSH_TOP : the absolute path where the iocsh.bash is executed
```
For example, one executes the iocsh.bash ```${HOME}``` to call ```e3_local/cmds``` via
```sh
$ iocsh.bash e3_local/cmds/iocStats.cmd
```
In this case,
```E3_CMD_TOP``` is defined as ```"${HOME}/e3_local/cmds"```
```E3_IOCSH_TOP``` is defined as ```"${HOME}"```
......@@ -16,7 +16,6 @@
# You should have received a copy of the GNU General Public License along with
# this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt
#
# Shell : setE3Env.bash
# Author : Jeong Han Lee
# email : jeonghan.lee@gmail.com
# date : Friday, November 8 22:48:29 CET 2019
......@@ -220,7 +219,7 @@ LD_LIBRARY_PATH=$(set_variable "${old_ld_path}" "${E3_LD_LIBRARY_PATH}")
export LD_LIBRARY_PATH
# Add iocsh.bash autocompletion
# Add iocsh autocompletion
# shellcheck source=require-ess/tools/iocsh_complete.bash
source "${SRC_PATH}"/iocsh_complete.bash
......