Merge 7.0.10 into PSI-7.0
This commit is contained in:
@@ -0,0 +1,376 @@
|
||||
# IOC Access Security
|
||||
|
||||
## ACF Syntax Forward Compatibility
|
||||
|
||||
EPICS 7.0.10 modified the Access Security Configuration File (ACF) parser to
|
||||
standardize the ACF grammar for forward compatibility.
|
||||
It did not change the syntax that was accepted by earlier versions of the parser,
|
||||
so existing access security configuration files would not need to be modified.
|
||||
All ACF definitions will adhere to a consistent syntax format,
|
||||
which will allow future additions to the access security language
|
||||
without breaking existing configurations.
|
||||
In practice, this means the structure of ACF files is now formally defined
|
||||
and will remain stable going forward,
|
||||
so any new grammar features will fit into the same pattern.
|
||||
(Existing ACF files continue to work as-is under the new parser,
|
||||
so no changes are required for legacy configurations or tools.).
|
||||
|
||||
**Generic ACF Syntax:**
|
||||
The ACF file consists of definitions for User Access Groups (UAG),
|
||||
Host Access Groups (HAG),
|
||||
and Access Security Groups (ASG),
|
||||
using the following general format
|
||||
(angle brackets below denote placeholders):
|
||||
|
||||
```text
|
||||
UAG(<name>) [{ <user> [, <user> ...] }]
|
||||
...
|
||||
|
||||
HAG(<name>) [{ <host> [, <host> ...] }]
|
||||
...
|
||||
|
||||
ASG(<name>) [{
|
||||
[INP<index>(<pvname>)
|
||||
...]
|
||||
|
||||
RULE(<level>, NONE | READ | WRITE [, NOTRAPWRITE | TRAPWRITE]) {
|
||||
[UAG(<name> [, <name> ...])]
|
||||
[HAG(<name> [, <name> ...])]
|
||||
[CALC(<calculation>)]
|
||||
}
|
||||
...
|
||||
}]
|
||||
...
|
||||
```
|
||||
|
||||
Under this schema each definition comprises a keyword,
|
||||
a name in parentheses,
|
||||
and (optionally) a braced block of contents.
|
||||
This uniform structure ensures that
|
||||
**future keywords or sections**
|
||||
can be introduced in the same form,
|
||||
maintaining compatibility with the parser.
|
||||
For example, if a new type of condition or group is added in a later release,
|
||||
it would follow the `KEYWORD(name) { ... }` pattern,
|
||||
so 7.0.10-era parsers can handle or ignore it gracefully
|
||||
instead of failing on unknown syntax.
|
||||
|
||||
**Supported Syntax in EPICS 7.0.10:**
|
||||
The current release defines the following specific elements
|
||||
within the above generic format:
|
||||
|
||||
- **UAG** -- *User Access Group*.
|
||||
Defines a group of user names.
|
||||
- **HAG** -- *Host Access Group*.
|
||||
Defines a group of host names
|
||||
(or IP addresses) that clients can connect from.
|
||||
- **ASG** -- *Access Security Group*.
|
||||
Defines a security group which records can be assigned to.
|
||||
An ASG entry may contain a block with input definitions and access rules.
|
||||
For example:
|
||||
|
||||
```text
|
||||
ASG(MyGroup) {
|
||||
INPA(myPV1)
|
||||
INPB(myPV2)
|
||||
RULE(1, WRITE) { ... }
|
||||
RULE(1, READ) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
If no rules are defined for an ASG,
|
||||
the access permissions default to always allowed.
|
||||
|
||||
- **INP<index>(<pvname>)** -- *Input link*.
|
||||
Declares an input process variable whose value can be used in a CALC condition.
|
||||
- **RULE(<level>, <permission> [, <logOption>]) { ... }** --
|
||||
Defines an access rule for the ASG.
|
||||
|
||||
Inside the curly braces of a RULE,
|
||||
**optional conditions** can restrict when that rule applies.
|
||||
All conditions that are present must be satisfied
|
||||
(they function as a logical AND):
|
||||
|
||||
- **UAG(<name>, ...)** -- User-group condition.
|
||||
The rule only applies if the Channel Access client's user
|
||||
is a member of one of the listed UAGs.
|
||||
- **HAG(<name>, ...)** -- Host-group condition.
|
||||
The rule only applies if the client's host
|
||||
(as determined by its IP or hostname) is in one of the listed HAGs
|
||||
- **CALC("<expression>")** -- Calculation condition.
|
||||
The rule only applies if the given expression evaluates to true (non-zero).
|
||||
|
||||
**Special Semantics for RULEs:**
|
||||
Rules will continue to allow the prescribed access if and only if
|
||||
all the predicates the rule contains are satisfied.
|
||||
- If the rule contains predicates that are unknown to the parser
|
||||
(indicating future functionality),
|
||||
then the rule will NOT not match,
|
||||
but no syntax error will be reported as long as the syntax is correct.
|
||||
- If the rule contains predicates that the parser does not recognise
|
||||
which are malformed (e.g. missing parentheses),
|
||||
then the rule will not match and the parser will report a syntax error.
|
||||
- In this way rules can be extended with new predicates
|
||||
without breaking older clients or giving those older clients elevated privileges.
|
||||
|
||||
**Special Semantics for unrecognised ACF file elements:**
|
||||
Any elements that are included in an ACF file will be ignored silently
|
||||
by a parser that does not understand them.
|
||||
- If an element is seen in an ACF file that is not understood by the parser,
|
||||
the parser will simply ignore it silently,
|
||||
without reporting an error,
|
||||
as long as its syntax is correct.
|
||||
- If elements are added to the ACF file that are malformed
|
||||
(e.g. missing parentheses),
|
||||
the parser will report a syntax error.
|
||||
- Thus new elements can be added to ACF files in new EPICS releases
|
||||
without breaking older clients that loads those files.
|
||||
|
||||
In summary, ACF forward compatibility means that from EPICS 7.0.10 onward,
|
||||
any new access security features will use this established syntax.
|
||||
The parser will recognize new group types or rule options using the same
|
||||
`<KEYWORD>(...) { ... }` convention,
|
||||
ensuring they can be used in files loaded by IOCs running EPICS 7.0.10 or later
|
||||
without being rejected by those IOCs or requiring their parser to be modified.
|
||||
This change does not require any modifications to existing ACF files --
|
||||
all legacy syntax remains valid,
|
||||
and the new standardized grammar provides a robust foundation for future extensions.
|
||||
|
||||
---
|
||||
|
||||
## Full Language Specification for Access Security Configuration Files
|
||||
|
||||
### Lexical tokens
|
||||
|
||||
**Ignored stuff**
|
||||
|
||||
- *Whitespace*: space, tab, `\r` (carriage return) -- may appear between tokens.
|
||||
|
||||
- *Newlines*: `\n` -- same as whitespace for syntax, but tracked for error messages.
|
||||
|
||||
- *Comments*: `#` ... end of line -- ignored.
|
||||
|
||||
**Terminals**
|
||||
|
||||
- `UAG` → literal string `"UAG"`
|
||||
- `HAG` → `"HAG"`
|
||||
- `ASG` → `"ASG"`
|
||||
- `RULE` → `"RULE"`
|
||||
- `CALC` → `"CALC"`
|
||||
- `INP(link)` → literal `"INP"` followed immediately by one uppercase letter `A`-`U`
|
||||
|
||||
```text
|
||||
link-letter ::= "A" | "B" | ... | "U"
|
||||
INP(link) ::= "INP" link-letter
|
||||
```
|
||||
|
||||
- `INT` → integer literal
|
||||
|
||||
```text
|
||||
INT ::= [ "+" | "-" ]? DIGIT+
|
||||
DIGIT ::= "0" | "1" | ... | "9"
|
||||
```
|
||||
|
||||
- `FLOAT` → floating-point literal with decimal point
|
||||
|
||||
```text
|
||||
FLOAT ::= [ "+" | "-" ]? DIGIT* "." DIGIT+ [ ( "e" | "E" ) [ "+" | "-" ] DIGIT+ ]?
|
||||
```
|
||||
|
||||
- `STRING` → either
|
||||
- **Unquoted**: One or more of
|
||||
|
||||
```text
|
||||
NAMECHAR ::= letter | digit | "_" | "-" | "+" | ":" | "." | "[" | "]" | "<" | ">" | ";"
|
||||
STRING(unquoted) ::= NAMECHAR+
|
||||
```
|
||||
|
||||
- **Quoted**: Surrounding quotes are stripped;
|
||||
escapes are kept literal at parse level.
|
||||
|
||||
```text
|
||||
STRING(quoted) ::= '"' { STRINGCHAR | ESCAPE } '"'
|
||||
STRINGCHAR ::= any char except '"' "\" "\n"
|
||||
ESCAPE ::= "\" any-char
|
||||
```
|
||||
|
||||
- Punctuation tokens (single-character terminals):
|
||||
|
||||
```text
|
||||
"(" ")" "{" "}" ","
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Grammar
|
||||
|
||||
#### Top level
|
||||
|
||||
```ebnf
|
||||
acf-file ::= asconfig ;
|
||||
|
||||
asconfig ::= asconfig-item { asconfig-item } ;
|
||||
|
||||
asconfig-item ::=
|
||||
uag-def
|
||||
| hag-def
|
||||
| asg-def
|
||||
| generic-top-level-item ;
|
||||
```
|
||||
|
||||
##### UAG / HAG groups
|
||||
|
||||
```ebnf
|
||||
uag-def ::= "UAG" uag-head [ uag-body ] ;
|
||||
|
||||
uag-head ::= "(" STRING ")" ;
|
||||
|
||||
uag-body ::= "{" uag-user-list "}" ;
|
||||
|
||||
uag-user-list ::= STRING { "," STRING } ;
|
||||
```
|
||||
|
||||
```ebnf
|
||||
hag-def ::= "HAG" hag-head [ hag-body ] ;
|
||||
|
||||
hag-head ::= "(" STRING ")" ;
|
||||
|
||||
hag-body ::= "{" hag-host-list "}" ;
|
||||
|
||||
hag-host-list ::= STRING { "," STRING } ;
|
||||
```
|
||||
|
||||
##### ASG (access security group)
|
||||
|
||||
```ebnf
|
||||
asg-def ::= "ASG" asg-head [ asg-body ] ;
|
||||
|
||||
asg-head ::= "(" STRING ")" ;
|
||||
|
||||
asg-body ::= "{" asg-body-item { asg-body-item } "}" ;
|
||||
|
||||
asg-body-item ::=
|
||||
inp-config
|
||||
| rule-config ;
|
||||
```
|
||||
|
||||
###### INP config
|
||||
|
||||
```ebnf
|
||||
inp-config ::= INP(link) "(" STRING ")" ;`
|
||||
```
|
||||
|
||||
###### RULE config
|
||||
|
||||
```ebnf
|
||||
rule-config ::= "RULE" rule-head [ rule-body ] ;
|
||||
|
||||
rule-head ::=
|
||||
"(" rule-head-mandatory "," rule-log-option ")"
|
||||
| "(" rule-head-mandatory ")" ;
|
||||
|
||||
rule-head-mandatory ::= INT "," STRING ;
|
||||
|
||||
rule-log-option ::= STRING ;
|
||||
```
|
||||
|
||||
```ebnf
|
||||
rule-body ::= "{" rule-list "}" ;
|
||||
|
||||
rule-list ::= rule-list-item { rule-list-item } ;
|
||||
|
||||
rule-list-item ::=
|
||||
"UAG" "(" rule-uag-list ")"
|
||||
| "HAG" "(" rule-hag-list ")"
|
||||
| "CALC" "(" STRING ")"
|
||||
| rule-generic-block-elem ;
|
||||
```
|
||||
|
||||
```ebnf
|
||||
rule-uag-list ::= STRING { "," STRING } ;
|
||||
|
||||
rule-hag-list ::= STRING { "," STRING } ;`
|
||||
```
|
||||
|
||||
##### Generic / future-proof syntax
|
||||
|
||||
These are the "catch-all" constructs that are **parsed** but currently **ignored** semantically.
|
||||
|
||||
###### Keyword classes
|
||||
|
||||
These are parser-level categories used inside generic constructs:
|
||||
|
||||
```ebnf
|
||||
keyword ::=
|
||||
"UAG"
|
||||
| "HAG"
|
||||
| "CALC"
|
||||
| non-rule-keyword ;
|
||||
|
||||
non-rule-keyword ::=
|
||||
"ASG"
|
||||
| "RULE"
|
||||
| INP(link) ; (* INPA..INPU *)
|
||||
```
|
||||
|
||||
###### Generic head (argument list)
|
||||
|
||||
```ebnf
|
||||
generic-head ::=
|
||||
"(" ")"
|
||||
| "(" generic-element ")"
|
||||
| "(" generic-list ")" ;
|
||||
|
||||
generic-list ::= generic-element "," generic-element { "," generic-element } ;
|
||||
|
||||
generic-element ::=
|
||||
keyword
|
||||
| STRING
|
||||
| INT
|
||||
| FLOAT ;
|
||||
```
|
||||
|
||||
###### Generic blocks
|
||||
|
||||
```ebnf
|
||||
generic-block ::=
|
||||
"{" generic-element "}"
|
||||
| "{" generic-list "}"
|
||||
| "{" generic-block-list "}" ;
|
||||
|
||||
generic-block-list ::= generic-block-elem { generic-block-elem } ;
|
||||
|
||||
generic-block-elem ::=
|
||||
generic-block-elem-name generic-head [ generic-block ] ;
|
||||
|
||||
generic-block-elem-name ::= keyword | STRING ;
|
||||
```
|
||||
|
||||
###### Generic top-level items
|
||||
|
||||
These are "unknown" top-level constructs, all of which are parsed and then ignored with a warning.
|
||||
|
||||
```ebnf
|
||||
generic-top-level-item ::=
|
||||
STRING generic-head generic-list-block
|
||||
| STRING generic-head generic-block
|
||||
| STRING generic-head ;
|
||||
```
|
||||
|
||||
where
|
||||
|
||||
```ebnf
|
||||
generic-list-block ::=
|
||||
"{" generic-element "}" "{" generic-list "}" ;
|
||||
```
|
||||
|
||||
###### Generic blocks inside RULE bodies
|
||||
|
||||
These are the "future predicates" in a RULE's body; they cause the RULE to be disabled with a warning, but they **must** still parse.
|
||||
|
||||
```ebnf
|
||||
rule-generic-block-elem ::=
|
||||
rule-generic-block-elem-name generic-head [ generic-block ] ;
|
||||
|
||||
rule-generic-block-elem-name ::= non-rule-keyword | STRING ;
|
||||
```
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
SRC_DIRS += $(LIBCOM)/as
|
||||
|
||||
DOCS += ACF-Language.md
|
||||
|
||||
INC += asLib.h
|
||||
INC += asTrapWrite.h
|
||||
|
||||
|
||||
@@ -202,6 +202,7 @@ typedef struct{
|
||||
ELLLIST uagList; /*List of ASGUAG*/
|
||||
ELLLIST hagList; /*List of ASGHAG*/
|
||||
int trapMask;
|
||||
int ignore; // 1 if rule to be ignored because of unknown elements
|
||||
} ASGRULE;
|
||||
typedef struct{
|
||||
ELLNODE node;
|
||||
|
||||
+173
-36
@@ -12,26 +12,44 @@ static int yyerror(char *);
|
||||
static int yy_start;
|
||||
#include "asLibRoutines.c"
|
||||
static int yyFailed = FALSE;
|
||||
static int yyWarned = FALSE;
|
||||
static int line_num=1;
|
||||
static UAG *yyUag=NULL;
|
||||
static HAG *yyHag=NULL;
|
||||
static ASG *yyAsg=NULL;
|
||||
static ASGRULE *yyAsgRule=NULL;
|
||||
|
||||
static
|
||||
char* yystrdup(const char *inp) {
|
||||
char* ret = strdup(inp);
|
||||
if(!ret)
|
||||
yyerror("MALLOC");
|
||||
return ret;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%start asconfig
|
||||
|
||||
%token tokenUAG tokenHAG tokenASG tokenRULE tokenCALC
|
||||
%token <Str> tokenINP
|
||||
%token <Int> tokenINTEGER
|
||||
%token <Str> tokenSTRING
|
||||
%token <Int64> tokenINT64 tokenINP
|
||||
%token <Float64> tokenFLOAT64
|
||||
|
||||
%union
|
||||
{
|
||||
int Int;
|
||||
epicsInt64 Int64;
|
||||
epicsFloat64 Float64;
|
||||
char *Str;
|
||||
}
|
||||
|
||||
%type <Str> non_rule_keyword
|
||||
%type <Str> generic_block_elem_name
|
||||
%type <Str> generic_block_elem
|
||||
%type <Str> rule_generic_block_elem
|
||||
%type <Str> rule_generic_block_elem_name
|
||||
%type <Str> keyword
|
||||
|
||||
%%
|
||||
|
||||
asconfig: asconfig asconfig_item
|
||||
@@ -43,13 +61,117 @@ asconfig_item: tokenUAG uag_head uag_body
|
||||
| tokenHAG hag_head
|
||||
| tokenASG asg_head asg_body
|
||||
| tokenASG asg_head
|
||||
| generic_item
|
||||
;
|
||||
|
||||
/* uniformally yield a string for use in warning messages */
|
||||
keyword: tokenUAG
|
||||
{ $$ = yystrdup("UAG"); }
|
||||
| tokenHAG
|
||||
{ $$ = yystrdup("HAG"); }
|
||||
| tokenCALC
|
||||
{ $$ = yystrdup("CALC"); }
|
||||
| non_rule_keyword
|
||||
;
|
||||
|
||||
non_rule_keyword: tokenASG
|
||||
{ $$ = yystrdup("ASG"); }
|
||||
| tokenRULE
|
||||
{ $$ = yystrdup("RULE"); }
|
||||
| tokenINP
|
||||
{
|
||||
if(!!($$ = yystrdup("INPA")))
|
||||
$$[3] += $1; /* 'A' + input number */
|
||||
}
|
||||
;
|
||||
|
||||
generic_item: tokenSTRING generic_head generic_list_block
|
||||
{
|
||||
yywarn("Ignoring unsupported TOP LEVEL nested block", $1);
|
||||
free($1);
|
||||
}
|
||||
| tokenSTRING generic_head generic_block
|
||||
{
|
||||
yywarn("Ignoring unsupported TOP LEVEL block", $1);
|
||||
free($1);
|
||||
}
|
||||
| tokenSTRING generic_head
|
||||
{
|
||||
yywarn("Ignoring unsupported TOP LEVEL bare block", $1);
|
||||
free($1);
|
||||
}
|
||||
;
|
||||
|
||||
generic_head: '(' ')'
|
||||
| '(' generic_element ')'
|
||||
| '(' generic_list ')'
|
||||
;
|
||||
|
||||
generic_list_block: '{' generic_element '}'
|
||||
'{' generic_list '}'
|
||||
;
|
||||
|
||||
generic_list: generic_list ',' generic_element
|
||||
| generic_element ',' generic_element
|
||||
;
|
||||
|
||||
generic_element: keyword
|
||||
| tokenSTRING
|
||||
{
|
||||
free($1);
|
||||
}
|
||||
| tokenINT64
|
||||
| tokenFLOAT64
|
||||
;
|
||||
|
||||
generic_block: '{' generic_element '}'
|
||||
| '{' generic_list '}'
|
||||
| '{' generic_block_list '}'
|
||||
;
|
||||
|
||||
generic_block_list: generic_block_list generic_block_elem
|
||||
{
|
||||
free($2);
|
||||
}
|
||||
| generic_block_elem
|
||||
{
|
||||
free($1);
|
||||
}
|
||||
;
|
||||
|
||||
generic_block_elem: generic_block_elem_name generic_head generic_block
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
| generic_block_elem_name generic_head
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
;
|
||||
|
||||
generic_block_elem_name: keyword
|
||||
| tokenSTRING
|
||||
;
|
||||
|
||||
rule_generic_block_elem: rule_generic_block_elem_name generic_head generic_block
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
| rule_generic_block_elem_name generic_head
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
;
|
||||
|
||||
rule_generic_block_elem_name: non_rule_keyword
|
||||
| tokenSTRING
|
||||
;
|
||||
|
||||
uag_head: '(' tokenSTRING ')'
|
||||
{
|
||||
yyUag = asUagAdd($2);
|
||||
if(!yyUag) yyerror("");
|
||||
free((void *)$2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -67,7 +189,7 @@ uag_user_list_name: tokenSTRING
|
||||
{
|
||||
if (asUagAddUser(yyUag,$1))
|
||||
yyerror("");
|
||||
free((void *)$1);
|
||||
free($1);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -75,7 +197,7 @@ hag_head: '(' tokenSTRING ')'
|
||||
{
|
||||
yyHag = asHagAdd($2);
|
||||
if(!yyHag) yyerror("");
|
||||
free((void *)$2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -90,7 +212,7 @@ hag_host_list_name: tokenSTRING
|
||||
{
|
||||
if (asHagAddHost(yyHag,$1))
|
||||
yyerror("");
|
||||
free((void *)$1);
|
||||
free($1);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -98,7 +220,7 @@ asg_head: '(' tokenSTRING ')'
|
||||
{
|
||||
yyAsg = asAsgAdd($2);
|
||||
if(!yyAsg) yyerror("");
|
||||
free((void *)$2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -114,49 +236,49 @@ asg_body_item: inp_config | rule_config
|
||||
|
||||
inp_config: tokenINP '(' tokenSTRING ')'
|
||||
{
|
||||
if (asAsgAddInp(yyAsg,$3,$<Int>1))
|
||||
if (asAsgAddInp(yyAsg,$3,(int)$<Int64>1))
|
||||
yyerror("");
|
||||
free((void *)$3);
|
||||
free($3);
|
||||
}
|
||||
;
|
||||
|
||||
rule_config: tokenRULE rule_head rule_body
|
||||
| tokenRULE rule_head
|
||||
|
||||
rule_head: rule_head_manditory rule_head_options
|
||||
rule_head: '(' rule_head_mandatory ',' rule_log_option ')'
|
||||
| '(' rule_head_mandatory ')'
|
||||
;
|
||||
|
||||
rule_head_manditory: '(' tokenINTEGER ',' tokenSTRING
|
||||
|
||||
rule_head_mandatory: tokenINT64 ',' tokenSTRING
|
||||
{
|
||||
asAccessRights rights;
|
||||
|
||||
if((strcmp($4,"NONE")==0)) {
|
||||
rights=asNOACCESS;
|
||||
} else if((strcmp($4,"READ")==0)) {
|
||||
rights=asREAD;
|
||||
} else if((strcmp($4,"WRITE")==0)) {
|
||||
rights=asWRITE;
|
||||
if ($1 < 0) {
|
||||
char message[60];
|
||||
sprintf(message, "RULE: LEVEL must be positive: %lld", $1);
|
||||
yyerror(message);
|
||||
} else if((strcmp($3,"NONE")==0)) {
|
||||
yyAsgRule = asAsgAddRule(yyAsg,asNOACCESS,(int)$1);
|
||||
} else if((strcmp($3,"READ")==0)) {
|
||||
yyAsgRule = asAsgAddRule(yyAsg,asREAD,(int)$1);
|
||||
} else if((strcmp($3,"WRITE")==0)) {
|
||||
yyAsgRule = asAsgAddRule(yyAsg,asWRITE,(int)$1);
|
||||
} else {
|
||||
yyerror("Access rights must be NONE, READ or WRITE");
|
||||
rights = asNOACCESS;
|
||||
yywarn("Ignoring RULE that contains an unsupported keyword", $3);
|
||||
}
|
||||
yyAsgRule = asAsgAddRule(yyAsg,rights,$2);
|
||||
free((void *)$4);
|
||||
free($3);
|
||||
}
|
||||
;
|
||||
|
||||
rule_head_options: ')'
|
||||
| rule_log_options
|
||||
|
||||
rule_log_options: ',' tokenSTRING ')'
|
||||
rule_log_option: tokenSTRING
|
||||
{
|
||||
if((strcmp($2,"TRAPWRITE")==0)) {
|
||||
if((strcmp($1,"TRAPWRITE")==0)) {
|
||||
long status;
|
||||
status = asAsgAddRuleOptions(yyAsgRule,AS_TRAP_WRITE);
|
||||
if(status) yyerror("");
|
||||
} else if((strcmp($2,"NOTRAPWRITE")!=0)) {
|
||||
} else if((strcmp($1,"NOTRAPWRITE")!=0)) {
|
||||
yyerror("Log options must be TRAPWRITE or NOTRAPWRITE");
|
||||
}
|
||||
free((void *)$2);
|
||||
free($1);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -173,7 +295,14 @@ rule_list_item: tokenUAG '(' rule_uag_list ')'
|
||||
{
|
||||
if (asAsgRuleCalc(yyAsgRule,$3))
|
||||
yyerror("");
|
||||
free((void *)$3);
|
||||
free($3);
|
||||
}
|
||||
| rule_generic_block_elem
|
||||
{
|
||||
yywarn("Ignoring RULE that contains an unsupported keyword", $1);
|
||||
free($1);
|
||||
if (asAsgRuleDisable(yyAsgRule))
|
||||
yyerror("");
|
||||
}
|
||||
;
|
||||
|
||||
@@ -185,7 +314,7 @@ rule_uag_list_name: tokenSTRING
|
||||
{
|
||||
if (asAsgRuleUagAdd(yyAsgRule,$1))
|
||||
yyerror("");
|
||||
free((void *)$1);
|
||||
free($1);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -197,7 +326,7 @@ rule_hag_list_name: tokenSTRING
|
||||
{
|
||||
if (asAsgRuleHagAdd(yyAsgRule,$1))
|
||||
yyerror("");
|
||||
free((void *)$1);
|
||||
free($1);
|
||||
}
|
||||
;
|
||||
%%
|
||||
@@ -207,12 +336,19 @@ rule_hag_list_name: tokenSTRING
|
||||
static int yyerror(char *str)
|
||||
{
|
||||
if (strlen(str))
|
||||
errlogPrintf("%s at line %d\n", str, line_num);
|
||||
fprintf(stderr, ERL_ERROR " %s at line %d\n", str, line_num);
|
||||
else
|
||||
errlogPrintf(ERL_ERROR " at line %d\n", line_num);
|
||||
fprintf(stderr, ERL_ERROR " at line %d\n", line_num);
|
||||
yyFailed = TRUE;
|
||||
return 0;
|
||||
}
|
||||
static int yywarn(char *str, char *token)
|
||||
{
|
||||
if (!yyWarned && strlen(str) && strlen(token))
|
||||
fprintf(stderr, ERL_WARNING " %s at line %d: %s\n", str, line_num, token);
|
||||
yyWarned = TRUE;
|
||||
return 0;
|
||||
}
|
||||
static int myParse(ASINPUTFUNCPTR inputfunction)
|
||||
{
|
||||
static int FirstFlag = 1;
|
||||
@@ -222,6 +358,7 @@ static int myParse(ASINPUTFUNCPTR inputfunction)
|
||||
if (!FirstFlag) {
|
||||
line_num=1;
|
||||
yyFailed = FALSE;
|
||||
yyWarned = FALSE;
|
||||
yyreset();
|
||||
yyrestart(NULL);
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ static long asAsgAddRuleOptions(ASGRULE *pasgrule,int trapMask);
|
||||
static long asAsgRuleUagAdd(ASGRULE *pasgrule,const char *name);
|
||||
static long asAsgRuleHagAdd(ASGRULE *pasgrule,const char *name);
|
||||
static long asAsgRuleCalc(ASGRULE *pasgrule,const char *calc);
|
||||
static long asAsgRuleDisable(ASGRULE *pasgrule);
|
||||
|
||||
/*
|
||||
asInitialize can be called while access security is already active.
|
||||
@@ -96,7 +97,7 @@ long epicsStdCall asInitialize(ASINPUTFUNCPTR inputfunction)
|
||||
HAGNAME *phagname;
|
||||
static epicsThreadOnceId asInitializeOnceFlag = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
epicsThreadOnce(&asInitializeOnceFlag,asInitializeOnce,(void *)0);
|
||||
epicsThreadOnce(&asInitializeOnceFlag,asInitializeOnce,NULL);
|
||||
LOCK;
|
||||
pasbasenew = asCalloc(1,sizeof(ASBASE));
|
||||
if(!freeListPvt) freeListInitPvt(&freeListPvt,sizeof(ASGCLIENT),20);
|
||||
@@ -585,8 +586,8 @@ int epicsStdCall asDumpFP(
|
||||
pasginp = (ASGINP *)ellNext(&pasginp->node);
|
||||
}
|
||||
while(pasgrule) {
|
||||
int print_end_brace;
|
||||
|
||||
int print_rule_end_brace = FALSE;
|
||||
if (pasgrule->ignore) goto next_rule;
|
||||
fprintf(fp,"\tRULE(%d,%s,%s)",
|
||||
pasgrule->level,asAccessName[pasgrule->access],
|
||||
asTrapOption[pasgrule->trapMask]);
|
||||
@@ -594,10 +595,10 @@ int epicsStdCall asDumpFP(
|
||||
pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList);
|
||||
if(pasguag || pasghag || pasgrule->calc) {
|
||||
fprintf(fp," {\n");
|
||||
print_end_brace = TRUE;
|
||||
print_rule_end_brace = TRUE;
|
||||
} else {
|
||||
fprintf(fp,"\n");
|
||||
print_end_brace = FALSE;
|
||||
print_rule_end_brace = FALSE;
|
||||
}
|
||||
if(pasguag) fprintf(fp,"\t\tUAG(");
|
||||
while(pasguag) {
|
||||
@@ -605,7 +606,6 @@ int epicsStdCall asDumpFP(
|
||||
pasguag = (ASGUAG *)ellNext(&pasguag->node);
|
||||
if(pasguag) fprintf(fp,","); else fprintf(fp,")\n");
|
||||
}
|
||||
pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList);
|
||||
if(pasghag) fprintf(fp,"\t\tHAG(");
|
||||
while(pasghag) {
|
||||
fprintf(fp,"%s",pasghag->phag->name);
|
||||
@@ -618,7 +618,8 @@ int epicsStdCall asDumpFP(
|
||||
fprintf(fp," result=%s",(pasgrule->result==1 ? "TRUE" : "FALSE"));
|
||||
fprintf(fp,"\n");
|
||||
}
|
||||
if(print_end_brace) fprintf(fp,"\t}\n");
|
||||
next_rule:
|
||||
if(print_rule_end_brace) fprintf(fp,"\t}\n");
|
||||
pasgrule = (ASGRULE *)ellNext(&pasgrule->node);
|
||||
}
|
||||
pasgmember = (ASGMEMBER *)ellFirst(&pasg->memberList);
|
||||
@@ -735,7 +736,7 @@ int epicsStdCall asDumpRulesFP(FILE *fp,const char *asgname)
|
||||
pasg = (ASG *)ellFirst(&pasbase->asgList);
|
||||
if(!pasg) fprintf(fp,"No ASGs\n");
|
||||
while(pasg) {
|
||||
int print_end_brace;
|
||||
int print_end_brace = FALSE;
|
||||
|
||||
if(asgname && strcmp(asgname,pasg->name)!=0) {
|
||||
pasg = (ASG *)ellNext(&pasg->node);
|
||||
@@ -761,8 +762,8 @@ int epicsStdCall asDumpRulesFP(FILE *fp,const char *asgname)
|
||||
pasginp = (ASGINP *)ellNext(&pasginp->node);
|
||||
}
|
||||
while(pasgrule) {
|
||||
int print_end_brace;
|
||||
|
||||
int print_rule_end_brace = FALSE;
|
||||
if ( pasgrule->ignore) goto next_rule;
|
||||
fprintf(fp,"\tRULE(%d,%s,%s)",
|
||||
pasgrule->level,asAccessName[pasgrule->access],
|
||||
asTrapOption[pasgrule->trapMask]);
|
||||
@@ -770,10 +771,10 @@ int epicsStdCall asDumpRulesFP(FILE *fp,const char *asgname)
|
||||
pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList);
|
||||
if(pasguag || pasghag || pasgrule->calc) {
|
||||
fprintf(fp," {\n");
|
||||
print_end_brace = TRUE;
|
||||
print_rule_end_brace = TRUE;
|
||||
} else {
|
||||
fprintf(fp,"\n");
|
||||
print_end_brace = FALSE;
|
||||
print_rule_end_brace = FALSE;
|
||||
}
|
||||
if(pasguag) fprintf(fp,"\t\tUAG(");
|
||||
while(pasguag) {
|
||||
@@ -793,7 +794,8 @@ int epicsStdCall asDumpRulesFP(FILE *fp,const char *asgname)
|
||||
fprintf(fp," result=%s",(pasgrule->result==1 ? "TRUE" : "FALSE"));
|
||||
fprintf(fp,"\n");
|
||||
}
|
||||
if(print_end_brace) fprintf(fp,"\t}\n");
|
||||
next_rule:
|
||||
if(print_rule_end_brace) fprintf(fp,"\t}\n");
|
||||
pasgrule = (ASGRULE *)ellNext(&pasgrule->node);
|
||||
}
|
||||
if(print_end_brace) fprintf(fp,"}\n");
|
||||
@@ -948,6 +950,7 @@ static long asComputeAsgPvt(ASG *pasg)
|
||||
if(!asActive) return(S_asLib_asNotActive);
|
||||
pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList);
|
||||
while(pasgrule) {
|
||||
if ( pasgrule->ignore) goto next_rule;
|
||||
double result = pasgrule->result; /* set for VAL */
|
||||
long status;
|
||||
|
||||
@@ -960,6 +963,8 @@ static long asComputeAsgPvt(ASG *pasg)
|
||||
pasgrule->result = ((result>.99) && (result<1.01)) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
next_rule:
|
||||
pasgrule = (ASGRULE *)ellNext(&pasgrule->node);
|
||||
}
|
||||
pasg->inpChanged = FALSE;
|
||||
@@ -995,6 +1000,7 @@ static long asComputePvt(ASCLIENTPVT asClientPvt)
|
||||
oldaccess=pasgclient->access;
|
||||
pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList);
|
||||
while(pasgrule) {
|
||||
if (pasgrule->ignore) goto next_rule;
|
||||
if(access == asWRITE) break;
|
||||
if(access>=pasgrule->access) goto next_rule;
|
||||
if(pasgclient->level > pasgrule->level) goto next_rule;
|
||||
@@ -1059,6 +1065,9 @@ void asFreeAll(ASBASE *pasbase)
|
||||
ASGUAG *pasguag;
|
||||
void *pnext;
|
||||
|
||||
if(!pasbase)
|
||||
return;
|
||||
|
||||
puag = (UAG *)ellFirst(&pasbase->uagList);
|
||||
while(puag) {
|
||||
puagname = (UAGNAME *)ellFirst(&puag->list);
|
||||
@@ -1408,3 +1417,14 @@ static long asAsgRuleCalc(ASGRULE *pasgrule,const char *calc)
|
||||
}
|
||||
return(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable a rule if it contains unsupported elements
|
||||
* @param pasgrule the rule to disable
|
||||
* @return Non-zero if rule was not disabled
|
||||
*/
|
||||
static long asAsgRuleDisable(ASGRULE *pasgrule) {
|
||||
if (!pasgrule) return 1;
|
||||
pasgrule->ignore = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ punctuation [(){},]
|
||||
link [A-U]
|
||||
|
||||
%{
|
||||
#include "epicsStdlib.h"
|
||||
|
||||
static ASINPUTFUNCPTR *my_yyinput;
|
||||
#undef YY_INPUT
|
||||
#define YY_INPUT(b,r,ms) (r=(*my_yyinput)((char *)b,ms))
|
||||
@@ -43,14 +45,31 @@ RULE { return(tokenRULE); }
|
||||
CALC { return(tokenCALC); }
|
||||
|
||||
INP{link} {
|
||||
yylval.Int = (unsigned char)yytext[3];
|
||||
yylval.Int -= 'A';
|
||||
yylval.Int64 = (unsigned char)yytext[3];
|
||||
yylval.Int64 -= 'A';
|
||||
return(tokenINP);
|
||||
}
|
||||
|
||||
{digit}+ { /*integer*/
|
||||
yylval.Int = atoi((char *)yytext);
|
||||
return(tokenINTEGER);
|
||||
[-+]?{digit}*\.{digit}+([eE][-+]?{digit}+)? {
|
||||
char *end;
|
||||
if (epicsParseDouble((char *)yytext, &yylval.Float64, &end) ) {
|
||||
char message[40];
|
||||
sprintf(message, "Error parsing Float64: %s", (char *)yytext);
|
||||
yyerror(message);
|
||||
} else {
|
||||
return(tokenFLOAT64);
|
||||
}
|
||||
}
|
||||
|
||||
[-+]?{digit}+ { /*integer 64*/
|
||||
char *end;
|
||||
if (epicsParseInt64((char *)yytext, &yylval.Int64, 10, &end) ) {
|
||||
char message[40];
|
||||
sprintf(message, "Error parsing Int64: %s", (char *)yytext);
|
||||
yyerror(message);
|
||||
} else {
|
||||
return(tokenINT64);
|
||||
}
|
||||
}
|
||||
|
||||
{name}+ { /*unquoted string*/
|
||||
@@ -60,7 +79,7 @@ INP{link} {
|
||||
|
||||
{doublequote}({stringchar}|{escape})*{doublequote} { /* quoted string */
|
||||
yylval.Str=asStrdup(yytext+1);
|
||||
yylval.Str[strlen(yylval.Str)-1] = '\0';
|
||||
yylval.Str[strlen(yylval.Str)-1] = '\0'; /* overwrite trailing '"' */
|
||||
return(tokenSTRING);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ static int cond_search(const char **ppinst, int match);
|
||||
|
||||
/* calcPerform
|
||||
*
|
||||
* Evalutate the postfix expression
|
||||
* Evaluate the postfix expression
|
||||
*/
|
||||
LIBCOM_API long
|
||||
calcPerform(double *parg, double *presult, const char *pinst)
|
||||
|
||||
@@ -42,7 +42,7 @@ typedef enum {
|
||||
UNARY_OPERATOR,
|
||||
VARARG_OPERATOR,
|
||||
BINARY_OPERATOR,
|
||||
SEPERATOR,
|
||||
SEPARATOR,
|
||||
CLOSE_PAREN,
|
||||
CONDITIONAL,
|
||||
EXPR_TERMINATOR,
|
||||
@@ -155,7 +155,7 @@ static const ELEMENT operators[] = {
|
||||
{"*", 5, 5, -1, BINARY_OPERATOR,MULT},
|
||||
{"**", 6, 6, -1, BINARY_OPERATOR,POWER},
|
||||
{"+", 4, 4, -1, BINARY_OPERATOR,ADD},
|
||||
{",", 0, 0, 0, SEPERATOR, NOT_GENERATED},
|
||||
{",", 0, 0, 0, SEPARATOR, NOT_GENERATED},
|
||||
{"-", 4, 4, -1, BINARY_OPERATOR,SUB},
|
||||
{"/", 5, 5, -1, BINARY_OPERATOR,DIV},
|
||||
{":", 0, 0, -1, CONDITIONAL, COND_ELSE},
|
||||
@@ -344,7 +344,7 @@ LIBCOM_API long
|
||||
operand_needed = TRUE;
|
||||
break;
|
||||
|
||||
case SEPERATOR:
|
||||
case SEPARATOR:
|
||||
if (pstacktop == stack) {
|
||||
*perror = CALC_ERR_BAD_SEPERATOR;
|
||||
goto bad;
|
||||
|
||||
+185
-168
@@ -22,7 +22,10 @@
|
||||
|
||||
#include "libComAPI.h"
|
||||
|
||||
/** \brief Number of input arguments to a calc expression (A-U) */
|
||||
/** \brief Number of input arguments to a calc expression (A-U)
|
||||
*
|
||||
* Since 7.0.10 the number of inputs has been increased from 12 to 21.
|
||||
*/
|
||||
#define CALCPERFORM_NARGS 21
|
||||
/** \brief Size of the internal partial result stack */
|
||||
#define CALCPERFORM_STACK 80
|
||||
@@ -41,20 +44,18 @@
|
||||
* few bytes smaller for some sizes.
|
||||
*
|
||||
* The maximum expansion from infix to postfix is for the sub-expression
|
||||
\code
|
||||
.1?.1:
|
||||
\endcode
|
||||
* which is 6 characters long and results in 21 bytes of postfix:
|
||||
\code
|
||||
* <tt>.1?.1:</tt> which is 6 characters long and results in 21 bytes of
|
||||
* postfix:
|
||||
\verbatim
|
||||
.1 => LITERAL_DOUBLE + 8 byte value
|
||||
? => COND_IF
|
||||
.1 => LITERAL_DOUBLE + 8 byte value
|
||||
: => COND_ELSE
|
||||
...
|
||||
=> COND_END
|
||||
\endcode
|
||||
\endverbatim
|
||||
* For other short expressions the factor 21/6 always gives a big enough
|
||||
* postfix buffer (proven by hand, look at '1+' and '.1+' as well).
|
||||
* postfix buffer (proven by hand, look at \c 1+ and <tt>.1+</tt> as well).
|
||||
*/
|
||||
#define INFIX_TO_POSTFIX_SIZE(n) ((n)*21/6)
|
||||
|
||||
@@ -115,205 +116,221 @@ extern "C" {
|
||||
|
||||
/** \brief Compile an infix expression into postfix byte-code
|
||||
*
|
||||
* Converts an expression from an infix string to postfix byte-code
|
||||
* Converts an expression from an infix string to postfix byte-code.
|
||||
*
|
||||
* \param pinfix Pointer to the infix string
|
||||
* \param ppostfix Pointer to the postfix buffer
|
||||
* \param perror Place to return an error code
|
||||
* \return Non-zero value in event of error
|
||||
*
|
||||
* It is the caller's responsibility to ensure that \c ppostfix points
|
||||
* to sufficient storage to hold the postfix expression. The macro
|
||||
* INFIX_TO_POSTFIX_SIZE(n) can be used to calculate an appropriate
|
||||
* postfix buffer size from the length of the infix buffer.
|
||||
* It is the caller's responsibility to ensure that \p ppostfix points to
|
||||
* sufficient storage to hold the postfix expression.
|
||||
* The macro INFIX_TO_POSTFIX_SIZE(n) can be used to calculate an
|
||||
* appropriate postfix buffer size from the length of the infix buffer.
|
||||
* The macro's parameter \p n must count the terminating nil byte too.
|
||||
*
|
||||
* \note "n" must count the terminating nil byte too.
|
||||
* -# The **infix expressions** that can be used are very similar to the
|
||||
* C expression syntax, but with some additions and subtle differences in
|
||||
* operator meaning and precedence.
|
||||
* The expression string may contain a series of expressions separated by
|
||||
* a semi-colon character <tt>;</tt> any one of which may actually provide
|
||||
* the calculation result.
|
||||
* However all of the other expressions included must assign their result
|
||||
* to a variable.
|
||||
* All alphabetic elements described below are case independent, so upper
|
||||
* and lower case letters may be used and mixed in the variable and
|
||||
* function names as desired.
|
||||
* Spaces may be used anywhere within an expression except between the
|
||||
* characters that make up a single expression element.
|
||||
|
||||
* -# The simplest expression element is a **numeric literal,** any
|
||||
* (positive) number expressed using the standard floating point syntax
|
||||
* that can be stored as a double precision value.
|
||||
* This now includes the values Infinity and NaN (not a number).
|
||||
* Note that negative numbers will be encoded as a positive literal, to
|
||||
* which the unary negate operator is applied.
|
||||
*
|
||||
* -# The **infix expressions** that can be used are very similar
|
||||
* to the C expression syntax, but with some additions and subtle
|
||||
* differences in operator meaning and precedence. The string may
|
||||
* contain a series of expressions separated by a semi-colon character ';'
|
||||
* any one of which may actually provide the calculation result; however
|
||||
* all of the other expressions included must assign their result to
|
||||
* a variable. All alphabetic elements described below are case independent,
|
||||
* so upper and lower case letters may be used and mixed in the variable
|
||||
* and function names as desired. Spaces may be used anywhere within an
|
||||
* expression except between the characters that make up a single expression element.
|
||||
* Examples:
|
||||
* - \c 1
|
||||
* - \c 2.718281828459
|
||||
* - \c Inf
|
||||
*
|
||||
* -# ***Numeric Literals***
|
||||
* The simplest expression element is a numeric literal, any (positive)
|
||||
* number expressed using the standard floating point syntax that can be stored
|
||||
* as a double precision value. This now includes the values Infinity and
|
||||
* NaN (not a number). Note that negative numbers will be encoded as a
|
||||
* positive literal to which the unary negate operator is applied.
|
||||
*
|
||||
* - Examples:
|
||||
* - 1
|
||||
* - 2.718281828459
|
||||
* - Inf
|
||||
*
|
||||
* -# ***Constants***
|
||||
* There are three trigonometric constants available to any expression
|
||||
* -# There are three **trigonometric constants** available to any expression
|
||||
* which return a value:
|
||||
* - pi returns the value of the mathematical constant pi.
|
||||
* - D2R evaluates to pi/180 which, when used as a multiplier,
|
||||
* converts an angle from degrees to radians.
|
||||
* - R2D evaluates to 180/pi which as a multiplier converts an angle
|
||||
* from radians to degrees.
|
||||
* - \c pi returns the value of the mathematical constant pi.
|
||||
* - \c D2R evaluates to pi/180 which, when used as a multiplier,
|
||||
* converts an angle from degrees to radians.
|
||||
* - \c R2D evaluates to 180/pi which as a multiplier converts an
|
||||
* angle from radians to degrees.
|
||||
*
|
||||
* -# ***Variables***
|
||||
* Variables are used to provide inputs to an expression, and are named
|
||||
* using the single letters A through U inclusive or the keyword VAL which
|
||||
* refers to the previous result of this calculation. The software that
|
||||
* makes use of the expression evaluation code should document how the
|
||||
* individual variables are given values; for the calc record type the input
|
||||
* links INPA through INPU can be used to obtain these from other record fields,
|
||||
* and VAL refers to the the VAL field (which can be overwritten from outside
|
||||
* the record via Channel Access or a database link).
|
||||
* -# **Variables** are used to provide inputs to an expression, and are
|
||||
* named using the single letters \c A through \c U inclusive or the
|
||||
* keyword \c VAL which refers to the previous result of this
|
||||
* calculation.
|
||||
* The software that makes use of the expression evaluation code should
|
||||
* document how the individual variables are given values.
|
||||
* For the calc and calcout record types the input links \c INPA through
|
||||
* \c INPU can be used to obtain values from other record fields, and \c
|
||||
* VAL refers to the the VAL field (which can be overwritten from
|
||||
* outside the record via Channel Access or a database link).
|
||||
*
|
||||
* -# ***Variable Assignment Operator***
|
||||
* Recently added is the ability to assign the result of a sub-expression to
|
||||
* any of the single letter variables, which can then be used in another
|
||||
* sub-expression. The variable assignment operator is the character pair
|
||||
* := and must immediately follow the name of the variable to receive the
|
||||
* expression value. Since the infix string must return exactly one value, every
|
||||
* -# The **Variable Assignment Operator** was added in 3.14.9 and
|
||||
* provides the ability to assign the result of a sub-expression to any
|
||||
* of the single letter variables, which can then be used in later
|
||||
* sub-expressions.
|
||||
* The variable assignment operator is the character pair <tt>:=</tt> and
|
||||
* must immediately follow the name of the variable to receive the
|
||||
* expression value.
|
||||
* Since the infix string must return exactly one value, every
|
||||
* expression string must have exactly one sub-expression that is not an
|
||||
* assignment, which can appear anywhere in the string. Sub-expressions within
|
||||
* the string are separated by a semi-colon character.
|
||||
* assignment, which can appear anywhere in the string.
|
||||
* Sub-expressions within the string are separated by a semi-colon
|
||||
* character <tt>;</tt> .
|
||||
*
|
||||
* - Examples:
|
||||
* - B; B:=A
|
||||
* - i:=i+1; a*sin(i*D2R)
|
||||
* Examples:
|
||||
* - <tt>B; B:=A</tt>
|
||||
* - <tt>i:=i+1; a*sin(i*D2R)</tt>
|
||||
*
|
||||
* -# ***Arithmetic Operators***
|
||||
* The usual binary arithmetic operators are provided: + - * and / with their
|
||||
* usual relative precedence and left-to-right associativity, and - may also
|
||||
* be used as a unary negate operator where it has a higher precedence and
|
||||
* associates from right to left. There is no unary plus operator, so numeric
|
||||
* literals cannot begin with a + sign.
|
||||
* -# The standard binary **Arithmetic Operators** are provided: <tt>+ -
|
||||
* *</tt> and \c / with their usual relative precedence and
|
||||
* left-to-right associativity.
|
||||
* A minus sign \c - may also be used as a unary negate operator where
|
||||
* it has a higher precedence and associates from right to left.
|
||||
* There is no unary plus operator, so numeric literals cannot begin
|
||||
* with a plus sign \c + .
|
||||
*
|
||||
* - Examples:
|
||||
* - a*b + c
|
||||
* - a/-4 - b
|
||||
* Examples:
|
||||
* - <tt>a*b + c</tt>
|
||||
* - <tt>a/-4 - b</tt>
|
||||
*
|
||||
* Three other binary operators are also provided: % is the integer modulo operator,
|
||||
* while the synonymous operators ** and ^ raise their left operand to the power of
|
||||
* the right operand. % has the same precedence and associativity as * and /, while
|
||||
* the power operators associate left-to-right and have a precedence in between * and
|
||||
* unary minus.
|
||||
* Three other binary operators are also provided:
|
||||
* \c % is the integer modulo operator, while the synonymous operators
|
||||
* \c ** and \c ^ raise their left operand to the power of the right
|
||||
* operand.
|
||||
* \c % has the same precedence and associativity as \c * and \c /,
|
||||
* while the power operators associate left-to-right and have a
|
||||
* precedence in between \c * and unary minus \c - .
|
||||
*
|
||||
* - Examples:
|
||||
* - e:=a%10
|
||||
* - d:=a/10%10
|
||||
* - c:=a/100%10
|
||||
* - b:=a/1000%10
|
||||
* - b*4096+c*256+d*16+e
|
||||
* - sqrt(a**2 + b**2)
|
||||
* Examples:
|
||||
* - <tt>e:=a%10</tt>
|
||||
* - <tt>d:=a/10%10</tt>
|
||||
* - <tt>c:=a/100%10</tt>
|
||||
* - <tt>b:=a/1000%10</tt>
|
||||
* - <tt>b*4096+c*256+d*16+e</tt>
|
||||
* - <tt>sqrt(a**2 + b**2)</tt>
|
||||
*
|
||||
* -# ***Algebraic Functions***
|
||||
* Various algebraic functions are available which take parameters inside
|
||||
* parentheses. The parameter separator is a comma.
|
||||
* -# Various **Algebraic Functions** are available which take parameters
|
||||
* inside parentheses.
|
||||
* The parameter separator is a comma <tt>,</tt> .
|
||||
*
|
||||
* - Absolute value: abs(a)
|
||||
* - Exponential ea: exp(a)
|
||||
* - Logarithm, base 10: log(a)
|
||||
* - Natural logarithm (base e): ln(a) or loge(a)
|
||||
* - n parameter maximum value: max(a, b, ...)
|
||||
* - n parameter minimum value: min(a, b, ...)
|
||||
* - Square root: sqr(a) or sqrt(a)
|
||||
* - Floating point modulo: fmod(num, den)
|
||||
* \since The fmod() function was added in 7.0.8
|
||||
* - Absolute value: \c abs(a)
|
||||
* - Exponential ea: \c exp(a)
|
||||
* - Logarithm, base 10: \c log(a)
|
||||
* - Natural logarithm (base e): \c ln(a) or \c loge(a)
|
||||
* - n parameter maximum value: <tt>max(a, b, ...)</tt>
|
||||
* - n parameter minimum value: <tt>min(a, b, ...)</tt>
|
||||
* - Square root: \c sqr(a) or \c sqrt(a)
|
||||
* - Floating point modulo: <tt>fmod(num, den)</tt>
|
||||
* <br>The \c fmod() function was added in 7.0.8
|
||||
*
|
||||
* -# ***Trigonometric Functions***
|
||||
* Standard circular trigonometric functions, with angles expressed in radians:
|
||||
* - Sine: sin(a)
|
||||
* - Cosine: cos(a)
|
||||
* - Tangent: tan(a)
|
||||
* - Arcsine: asin(a)
|
||||
* - Arccosine: acos(a)
|
||||
* - Arctangent: atan(a)
|
||||
* - 2 parameter arctangent: atan2(a, b)
|
||||
* \note Note that these arguments are the reverse of the ANSI C function,
|
||||
* so while C would return arctan(a/b) the calc expression engine returns arctan(b/a)
|
||||
* -# Standard circular **Trigonometric Functions** exist with angles
|
||||
* expressed in radians:
|
||||
*
|
||||
* -# ***Hyperbolic Trigonometry***
|
||||
* The basic hyperbolic functions are provided, but no inverse functions
|
||||
* (which are not provided by the ANSI C math library either).
|
||||
* - Hyperbolic sine: sinh(a)
|
||||
* - Hyperbolic cosine: cosh(a)
|
||||
* - Hyperbolic tangent: tanh(a)
|
||||
* - Sine: \c sin(a)
|
||||
* - Cosine: \c cos(a)
|
||||
* - Tangent: \c tan(a)
|
||||
* - Arcsine: \c asin(a)
|
||||
* - Arccosine: \c acos(a)
|
||||
* - Arctangent: \c atan(a)
|
||||
* - 2 parameter arctangent: <tt>atan2(a, b)</tt>
|
||||
* <br>Note that the \c atan2 arguments are the reverse of the ANSI C
|
||||
* function, so while C would return \c arctan(a/b) the calc
|
||||
* expression engine returns \c arctan(b/a)
|
||||
*
|
||||
* -# ***Numeric Functions***
|
||||
* The numeric functions perform operations related to the floating point
|
||||
* numeric representation and truncation or rounding.
|
||||
* - Round up to next integer: ceil(a)
|
||||
* - Round down to next integer: floor(a)
|
||||
* - Round to nearest integer: nint(a)
|
||||
* - Test for infinite result: isinf(a)
|
||||
* - Test for any non-numeric values: isnan(a, ...)
|
||||
* - Test for all finite, numeric values: finite(a, ...)
|
||||
* - Random number between 0 and 1: rndm
|
||||
* -# The basic **Hyperbolic Trigonometry** functions are provided, but
|
||||
* no inverse functions (which aren't provided by the ANSI C math
|
||||
* library either).
|
||||
*
|
||||
* -# ***Boolean Operators***
|
||||
* These operators regard their arguments as true or false, where 0.0 is
|
||||
* false and any other value is true.
|
||||
* - Hyperbolic sine: \c sinh(a)
|
||||
* - Hyperbolic cosine: \c cosh(a)
|
||||
* - Hyperbolic tangent: \c tanh(a)
|
||||
*
|
||||
* - Boolean and: a && b
|
||||
* - Boolean or: a || b
|
||||
* - Boolean not: !a
|
||||
* -# These **Numeric Functions** perform operations related to the
|
||||
* floating point numeric representation and truncation or rounding.
|
||||
*
|
||||
* -# ***Bitwise Operators***
|
||||
* Most bitwise operators convert their arguments to 32-bit signed integer (by
|
||||
* truncation), perform the appropriate bitwise operation, then convert back
|
||||
* to a floating point value. The arithmetic right shift operator >> thus
|
||||
* retains the sign bit of the left-hand argument. The logical right shift
|
||||
* operator >>> is performed on an unsigned integer though, so injects zeros
|
||||
* while shifting. The right-hand shift argument is masked so only the lower
|
||||
* 5 bits are used. Unlike in C, ^ is not a bitwise exclusive-or operator.
|
||||
* - Round up to next integer: \c ceil(a)
|
||||
* - Round down to next integer: \c floor(a)
|
||||
* - Round to nearest integer: \c nint(a)
|
||||
* - Test for infinite result: \c isinf(a)
|
||||
* - Test for any non-numeric values: <tt>isnan(a, ...)</tt>
|
||||
* - Test for all finite, numeric values: <tt>finite(a, ...)</tt>
|
||||
* - Random number between 0 and 1: \c rndm
|
||||
*
|
||||
* - Bitwise and: a & b or a and b
|
||||
* - Bitwise or: a | b or a or b
|
||||
* - Bitwise exclusive or: a xor b
|
||||
* - Bitwise not (ones complement): ~a or not a
|
||||
* - Arithmetic left shift: a << b
|
||||
* - Arithmetic right shift: a >> b
|
||||
* - Logical right shift: a >>> b
|
||||
* -# The **Boolean Operators** evaluate their arguments as true or
|
||||
* false, where \c 0.0 is false and any other value is true.
|
||||
*
|
||||
* -# ***Relational Operators***
|
||||
* Standard numeric comparisons between two values:
|
||||
* - Boolean and: <tt>a && b</tt>
|
||||
* - Boolean or: <tt>a || b</tt>
|
||||
* - Boolean not: \c !a
|
||||
*
|
||||
* - Less than: a < b
|
||||
* - Less than or equal to: a <= b
|
||||
* - Equal to: a = b or a == b
|
||||
* - Greater than or equal to: a >= b
|
||||
* - Greater than: a > b
|
||||
* - Not equal to: a != b or a # b
|
||||
* -# Most **Bitwise Operators** convert their arguments to 32-bit signed
|
||||
* integer (by truncation), perform the appropriate bitwise operation,
|
||||
* then convert back to a floating point value.
|
||||
* The arithmetic right shift operator \c >> thus retains the sign bit of
|
||||
* the left-hand argument.
|
||||
* The logical right shift operator \c >>> is performed on an unsigned
|
||||
* integer though, so it injects zeros while shifting.
|
||||
* The right-hand shift argument is masked so only the lower 5 bits are
|
||||
* used.
|
||||
* Unlike in C, \c ^ is not a bitwise exclusive-or operator.
|
||||
*
|
||||
* -# ***Conditional Operator***
|
||||
* Expressions can use the C conditional operator, which has a lower
|
||||
* precedence than all of the other operators except for the assignment operator.
|
||||
* - Bitwise and: <tt>a & b</tt> or <tt>a and b</tt>
|
||||
* - Bitwise or: <tt>a | b</tt> or <tt>a or b</tt>
|
||||
* - Bitwise exclusive or: <tt>a xor b</tt>
|
||||
* - Bitwise not (ones complement): <tt>~a</tt> or <tt>not a</tt>
|
||||
* - Arithmetic left shift: <tt>a << b</tt>
|
||||
* - Arithmetic right shift: <tt>a >> b</tt>
|
||||
* - Logical right shift: <tt>a >>> b</tt>
|
||||
*
|
||||
* - condition ? true result : false result
|
||||
* - Example:
|
||||
* - a < 360 ? a+1 : 0
|
||||
* -# The **Relational Operators** perform numeric comparisons between
|
||||
* two double-precision values:
|
||||
*
|
||||
* -# ***Parentheses***
|
||||
* Sub-expressions can be placed within parentheses to override operator presence rules.
|
||||
* Parentheses can be nested to any depth, but the intermediate value stack used by
|
||||
* the expression evaluation engine is limited to 80 results (which require an
|
||||
* expression at least 321 characters long to reach).
|
||||
* - Less than: <tt>a < b</tt>
|
||||
* - Less than or equal to: <tt>a <= b</tt>
|
||||
* - Equal to: <tt>a = b</tt> or <tt>a == b</tt>
|
||||
* - Greater than or equal to: <tt>a >= b</tt>
|
||||
* - Greater than: <tt>a > b</tt>
|
||||
* - Not equal to: <tt>a != b</tt> or <tt>a # b</tt>
|
||||
*
|
||||
* -# Expressions can use the C **Conditional Operator**, which has a
|
||||
* lower precedence than all of the other operators except for the
|
||||
* assignment operator.
|
||||
*
|
||||
* - \a condition <tt>?</tt> \a true-expression <tt>:</tt>
|
||||
* \a false-expression
|
||||
* - Example:
|
||||
* <tt>a < 360 ? a+1 : 0</tt>
|
||||
*
|
||||
* -# Sub-expressions can be placed within **Parentheses** <tt>()</tt>
|
||||
* to override operator presence rules.
|
||||
* Parentheses can be nested to any depth, but the intermediate value
|
||||
* stack used by the expression evaluation engine is limited to 80
|
||||
* results (which takes an expression at least 321 characters long to
|
||||
* reach).
|
||||
*/
|
||||
LIBCOM_API long
|
||||
postfix(const char *pinfix, char *ppostfix, short *perror);
|
||||
|
||||
/** \brief Run the calculation engine
|
||||
*
|
||||
* Evaluates the postfix expression against a set ot input values.
|
||||
* Evaluates the postfix expression against a set of input values.
|
||||
*
|
||||
* \param parg Pointer to an array of double values for the arguments A-U
|
||||
* that can appear in the expression. Note that the argument values may be
|
||||
* modified if the expression uses the assignment operator.
|
||||
* \param presult Where to put the calculated result, which may be a NaN or Infinity.
|
||||
* \param parg Pointer to an array of double values for the arguments
|
||||
* \c A-U that can appear in the expression.
|
||||
* Note that the argument values may be modified if the expression uses
|
||||
* the assignment operator.
|
||||
* \param presult Where to put the calculated result, which may be a NaN
|
||||
* or Infinity.
|
||||
* \param ppostfix The postfix expression created by postfix().
|
||||
* \return Status value 0 for OK, or non-zero if an error is discovered
|
||||
* during the evaluation process.
|
||||
|
||||
@@ -423,7 +423,7 @@ void resTable<T,ID>::show ( unsigned level ) const
|
||||
mean, stdDev, maxEntries );
|
||||
printf("%u empty buckets\n", empty);
|
||||
if ( X != this->nInUse ) {
|
||||
printf ("this->nInUse didnt match items counted which was %f????\n", X );
|
||||
printf ("this->nInUse didn't match items counted which was %f????\n", X );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1106,7 +1106,7 @@ stringId::stringId (const char * idIn, allocationType typeIn) :
|
||||
if (typeIn==copyString) {
|
||||
unsigned nChars = strlen (idIn) + 1u;
|
||||
this->pStr = new char [nChars];
|
||||
memcpy ( (void *) this->pStr, idIn, nChars );
|
||||
memcpy ( const_cast<char*>(this->pStr), idIn, nChars );
|
||||
}
|
||||
else {
|
||||
this->pStr = idIn;
|
||||
@@ -1140,7 +1140,7 @@ stringId::~stringId()
|
||||
//
|
||||
// the HP-UX compiler gives us a warning on
|
||||
// each cast away of const, but in this case
|
||||
// it cant be avoided.
|
||||
// it can't be avoided.
|
||||
//
|
||||
// The DEC compiler complains that const isn't
|
||||
// really significant in a cast if it is present.
|
||||
|
||||
@@ -78,7 +78,7 @@ int dbmfInit(size_t size, int chunkItems)
|
||||
pdbmfPvt = &dbmfPvt;
|
||||
ellInit(&pdbmfPvt->chunkList);
|
||||
pdbmfPvt->lock = epicsMutexMustCreate();
|
||||
/*allign to at least a double*/
|
||||
/*align to at least a double*/
|
||||
pdbmfPvt->size = size + size%sizeof(double);
|
||||
/* layout is
|
||||
* | itemHeader | REDZONE | size | REDZONE |
|
||||
@@ -126,7 +126,7 @@ void* dbmfMalloc(size_t size)
|
||||
pitemHeader = (itemHeader *)pmem;
|
||||
pitemHeader->pchunkNode = pchunkNode;
|
||||
pnextFree = &pitemHeader->pnextFree;
|
||||
*pnextFree = *pfreeList; *pfreeList = (void *)pmem;
|
||||
*pnextFree = *pfreeList; *pfreeList = pmem;
|
||||
pdbmfPvt->nFree++;
|
||||
pmem += pdbmfPvt->allocSize;
|
||||
}
|
||||
@@ -154,7 +154,7 @@ void* dbmfMalloc(size_t size)
|
||||
epicsMutexUnlock(pdbmfPvt->lock);
|
||||
pmem += sizeof(itemHeader) + REDZONE;
|
||||
VALGRIND_MEMPOOL_ALLOC(pdbmfPvt, pmem, size);
|
||||
return((void *)pmem);
|
||||
return pmem;
|
||||
}
|
||||
|
||||
char * dbmfStrdup(const char *str)
|
||||
@@ -190,7 +190,7 @@ void dbmfFree(void* mem)
|
||||
pitemHeader = (itemHeader *)pmem;
|
||||
if(!pitemHeader->pchunkNode) {
|
||||
if(dbmfDebug) printf("dbmfGree: mem %p\n",pmem);
|
||||
free((void *)pmem); pdbmfPvt->nAlloc--;
|
||||
free(pmem); pdbmfPvt->nAlloc--;
|
||||
}else {
|
||||
void **pfreeList = &pdbmfPvt->freeList;
|
||||
void **pnextFree = &pitemHeader->pnextFree;
|
||||
@@ -221,7 +221,7 @@ int dbmfShow(int level)
|
||||
pchunkNode = (chunkNode *)ellFirst(&pdbmfPvt->chunkList);
|
||||
while(pchunkNode) {
|
||||
printf("pchunkNode %p nNotFree %d\n",
|
||||
(void*)pchunkNode,pchunkNode->nNotFree);
|
||||
pchunkNode,pchunkNode->nNotFree);
|
||||
pchunkNode = (chunkNode *)ellNext(&pchunkNode->node);
|
||||
}
|
||||
}
|
||||
@@ -229,10 +229,10 @@ int dbmfShow(int level)
|
||||
void **pnextFree;;
|
||||
|
||||
epicsMutexMustLock(pdbmfPvt->lock);
|
||||
pnextFree = (void**)pdbmfPvt->freeList;
|
||||
pnextFree = pdbmfPvt->freeList;
|
||||
while(pnextFree) {
|
||||
printf("%p\n",*pnextFree);
|
||||
pnextFree = (void**)*pnextFree;
|
||||
pnextFree = *pnextFree;
|
||||
}
|
||||
epicsMutexUnlock(pdbmfPvt->lock);
|
||||
}
|
||||
|
||||
@@ -146,6 +146,8 @@ LIBCOM_API ELLNODE * ellGet (ELLLIST *pList);
|
||||
* \brief Deletes and returns the last node from a list.
|
||||
* \param pList Pointer to list from which to get node
|
||||
* \return Pointer to the last node from the list, or NULL if the list is empty
|
||||
*
|
||||
* \since 3.15.0.1
|
||||
*/
|
||||
LIBCOM_API ELLNODE * ellPop (ELLLIST *pList);
|
||||
/**
|
||||
@@ -192,6 +194,8 @@ typedef int (*pListCmp)(const ELLNODE* A, const ELLNODE* B);
|
||||
*
|
||||
* \note Use of mergesort algorithm based on analysis by
|
||||
* http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
|
||||
*
|
||||
* \since 3.15.5
|
||||
*/
|
||||
LIBCOM_API void ellSortStable(ELLLIST *pList, pListCmp pListCmp);
|
||||
/**
|
||||
|
||||
Vendored
+11
-1
@@ -78,6 +78,13 @@ LIBCOM_API extern const ENV_PARAM IOCSH_HISTSIZE;
|
||||
LIBCOM_API extern const ENV_PARAM IOCSH_HISTEDIT_DISABLE;
|
||||
LIBCOM_API extern const ENV_PARAM EPICS_MUTEX_USE_PRIORITY_INHERITANCE;
|
||||
LIBCOM_API extern const ENV_PARAM EPICS_ABORT_ON_ASSERT;
|
||||
LIBCOM_API extern const ENV_PARAM EPICS_ALLOW_POSIX_THREAD_PRIORITY_SCHEDULING;
|
||||
/** @brief List of all parameters.
|
||||
*
|
||||
* A NULL terminated array of all ENV_PARAM known to EPICS Base.
|
||||
* This array is assembled during the EPICS Base build, and
|
||||
* contains at least the preceding parameters.
|
||||
*/
|
||||
LIBCOM_API extern const ENV_PARAM *env_param_list[];
|
||||
|
||||
struct in_addr;
|
||||
@@ -92,10 +99,13 @@ struct in_addr;
|
||||
* is set to '\0' and NULL is returned.
|
||||
*
|
||||
* \param pParam Pointer to config param structure.
|
||||
* \param bufDim Dimension of parameter buffer
|
||||
* \param bufDim Dimension of parameter buffer.
|
||||
* Must be greater than zero.
|
||||
* \param pBuf Pointer to parameter buffer
|
||||
* \return Pointer to the environment variable value string, or
|
||||
* NULL if no parameter value and default value was empty.
|
||||
*
|
||||
* \post A terminating nil will be written to pBuf.
|
||||
*/
|
||||
LIBCOM_API char * epicsStdCall
|
||||
envGetConfigParam(const ENV_PARAM *pParam, int bufDim, char *pBuf);
|
||||
|
||||
@@ -45,7 +45,7 @@ extern "C" {
|
||||
*
|
||||
* Handles EPICS message codes, and "errno" codes.
|
||||
*
|
||||
* Copies in a mesage for any status code. Unknown status codes
|
||||
* Copies in a message for any status code. Unknown status codes
|
||||
* are printed numerically.
|
||||
*/
|
||||
LIBCOM_API void errSymLookup(long status, char *pBuf, size_t bufLength);
|
||||
@@ -65,7 +65,7 @@ LIBCOM_API void errSymTest(epicsUInt16 modnum, epicsUInt16 begErrNum,
|
||||
LIBCOM_API void errSymTestPrint(long errNum);
|
||||
LIBCOM_API int errSymBld(void);
|
||||
/** @brief Define new custom error code and associate message string.
|
||||
* @param errNum New error code. Caller is reponsible for avoiding reuse of existing codes.
|
||||
* @param errNum New error code. Caller is responsible for avoiding reuse of existing codes.
|
||||
* @param message New message. Pointer stored. Caller must not free pointed storage.
|
||||
* @return 0 on success
|
||||
*/
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
// 1) This library is not thread safe
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define instantiateRecourceLib
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsThread.h"
|
||||
@@ -187,19 +189,12 @@ LIBCOM_API void fdManager::process(double delay)
|
||||
++ioPending;
|
||||
|
||||
#ifdef FDMGR_USE_POLL
|
||||
#if __cplusplus >= 201100L
|
||||
priv->pollfds.emplace_back(pollfd{
|
||||
.fd = iter->getFD(),
|
||||
.events = WIN_POLLEVENT_FILTER(PollEvents[iter->getType()])
|
||||
});
|
||||
#else
|
||||
struct pollfd pollfd;
|
||||
pollfd.fd = iter->getFD();
|
||||
pollfd.events = WIN_POLLEVENT_FILTER(PollEvents[iter->getType()]);
|
||||
pollfd.revents = 0;
|
||||
priv->pollfds.push_back(pollfd);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef FDMGR_USE_SELECT
|
||||
FD_SET(iter->getFD(), &priv->fdSets[iter->getType()]);
|
||||
@@ -372,11 +367,15 @@ fdReg::~fdReg()
|
||||
//
|
||||
void fdReg::show(unsigned level) const
|
||||
{
|
||||
printf("fdReg at %p\n", this);
|
||||
if (level > 1u) {
|
||||
printf("\tstate = %d, onceOnly = %d\n",
|
||||
state, onceOnly);
|
||||
}
|
||||
std::cout << "fdReg at " << this << "\n";
|
||||
if (level > 1)
|
||||
std::cout << "\tstate = " << (
|
||||
state == active ? "active" :
|
||||
state == pending ? "pending" :
|
||||
state == limbo ? "limbo" :
|
||||
"invalid")
|
||||
<< ", onceOnly = " << (onceOnly ? "true" : "false")
|
||||
<< "\n";
|
||||
fdRegId::show(level);
|
||||
}
|
||||
|
||||
@@ -385,15 +384,17 @@ void fdReg::show(unsigned level) const
|
||||
//
|
||||
void fdRegId::show(unsigned level) const
|
||||
{
|
||||
printf("fdRegId at %p\n", this);
|
||||
if (level > 1u) {
|
||||
printf("\tfd = %"
|
||||
#if defined(_WIN32)
|
||||
"I"
|
||||
#endif
|
||||
"d, type = %d\n",
|
||||
fd, type);
|
||||
std::cout << "fdRegId at " << this << "\n";
|
||||
if (level > 1) {
|
||||
std::cout << "\tfd = " << fd
|
||||
<< ", type = " << (
|
||||
type == fdrRead ? "fdrRead" :
|
||||
type == fdrWrite ? "fdrWrite" :
|
||||
type == fdrException ? "fdrException" :
|
||||
"invalid")
|
||||
<< "\n";
|
||||
}
|
||||
std::cout << std::flush;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -71,7 +71,7 @@ LIBCOM_API fdctx * epicsStdCall fdmgr_init(void);
|
||||
* Specify a function to be called with a specified parameter
|
||||
* after a specified delay relative to the current time
|
||||
*
|
||||
* Returns fdmgrNoAlarm (zero) if alarm cant be created
|
||||
* Returns fdmgrNoAlarm (zero) if alarm can't be created
|
||||
*/
|
||||
#define fdmgrNoAlarm 0
|
||||
LIBCOM_API fdmgrAlarmId epicsStdCall fdmgr_add_timeout(
|
||||
|
||||
@@ -41,7 +41,7 @@ Changes between 2.3 Patch #2 (02Aug90) and original 2.3 release:
|
||||
Reordered #ifdef maze in the scanner skeleton in the hope of
|
||||
getting the declarations right for cfront and g++, too.
|
||||
|
||||
- Note that this patch supercedes patch #1 for release 2.3,
|
||||
- Note that this patch supersedes patch #1 for release 2.3,
|
||||
which was never announced but was available briefly for
|
||||
anonymous ftp.
|
||||
|
||||
@@ -56,7 +56,7 @@ Changes between 2.3 (full) release of 28Jun90 and 2.2 (alpha) release:
|
||||
given. To specify an end-of-file action for just the initial
|
||||
state, use <INITIAL><<EOF>>.
|
||||
|
||||
- -d debug output is now contigent on the global yy_flex_debug
|
||||
- -d debug output is now contingent on the global yy_flex_debug
|
||||
being set to a non-zero value, which it is by default.
|
||||
|
||||
- A new macro, YY_USER_INIT, is provided for the user to specify
|
||||
@@ -99,7 +99,7 @@ Changes between 2.3 (full) release of 28Jun90 and 2.2 (alpha) release:
|
||||
- yy_switch_to_buffer() can be used in the yywrap() macro/routine.
|
||||
|
||||
- flex scanners do not use stdio for their input, and hence when
|
||||
writing an interactive scanner one must explictly call fflush()
|
||||
writing an interactive scanner one must explicitly call fflush()
|
||||
after writing out a prompt.
|
||||
|
||||
- flex scanner can be made reentrant (after a fashion) by using
|
||||
|
||||
@@ -341,7 +341,7 @@
|
||||
|
||||
- yy_create_buffer( file, size ) takes a <I>FILE</I> pointer and
|
||||
an integer <I>size</I>. It returns a YY_BUFFER_STATE handle to
|
||||
a new input buffer large enough to accomodate <I>size</I>
|
||||
a new input buffer large enough to accommodate <I>size</I>
|
||||
characters and associated with the given file. When in
|
||||
doubt, use YY_BUF_SIZE for the size.
|
||||
|
||||
|
||||
@@ -574,7 +574,7 @@ extern void *reallocate_array(void *array, int size, int element_size);
|
||||
(int *) allocate_array( size, sizeof( int ) )
|
||||
|
||||
#define reallocate_integer_array(array,size) \
|
||||
(int *) reallocate_array( (void *) array, size, sizeof( int ) )
|
||||
(int *) reallocate_array( array, size, sizeof( int ) )
|
||||
|
||||
#define allocate_int_ptr_array(size) \
|
||||
(int **) allocate_array( size, sizeof( int * ) )
|
||||
@@ -587,22 +587,22 @@ extern void *reallocate_array(void *array, int size, int element_size);
|
||||
allocate_array( size, sizeof( union dfaacc_union ) )
|
||||
|
||||
#define reallocate_int_ptr_array(array,size) \
|
||||
(int **) reallocate_array( (void *) array, size, sizeof( int * ) )
|
||||
(int **) reallocate_array( array, size, sizeof( int * ) )
|
||||
|
||||
#define reallocate_char_ptr_array(array,size) \
|
||||
(char **) reallocate_array( (void *) array, size, sizeof( char * ) )
|
||||
(char **) reallocate_array( array, size, sizeof( char * ) )
|
||||
|
||||
#define reallocate_dfaacc_union(array, size) \
|
||||
(union dfaacc_union *) \
|
||||
reallocate_array( (void *) array, size, sizeof( union dfaacc_union ) )
|
||||
reallocate_array( array, size, sizeof( union dfaacc_union ) )
|
||||
|
||||
#define allocate_character_array(size) \
|
||||
(Char *) allocate_array( size, sizeof( Char ) )
|
||||
|
||||
#define reallocate_character_array(array,size) \
|
||||
(Char *) reallocate_array( (void *) array, size, sizeof( Char ) )
|
||||
(Char *) reallocate_array( array, size, sizeof( Char ) )
|
||||
|
||||
#if 0 /* JRW this might couse trouble... but not for IOC usage */
|
||||
#if 0 /* JRW this might cause trouble... but not for IOC usage */
|
||||
/* used to communicate between scanner and parser. The type should really
|
||||
* be YYSTYPE, but we can't easily get our hands on it.
|
||||
*/
|
||||
@@ -710,7 +710,7 @@ extern void lerrsf (char[], char[]) NORETURN;
|
||||
/* spit out a "# line" statement */
|
||||
extern void line_directive_out (FILE*);
|
||||
|
||||
/* generate a data statment for a two-dimensional array */
|
||||
/* generate a data statement for a two-dimensional array */
|
||||
extern void mk2data (int);
|
||||
|
||||
/* generate a data statement */
|
||||
|
||||
@@ -76,7 +76,7 @@ void *allocate_array(int size, int element_size)
|
||||
if ( element_size * size <= 0 )
|
||||
flexfatal( "request for < 1 byte in allocate_array()" );
|
||||
|
||||
mem = (void *) malloc( (unsigned) (element_size * size) );
|
||||
mem = malloc( (size_t) element_size * size );
|
||||
|
||||
if ( mem == NULL )
|
||||
flexfatal( "memory allocation failed in allocate_array()" );
|
||||
@@ -705,7 +705,7 @@ void *reallocate_array(void *array, int size, int element_size)
|
||||
flexfatal( "attempt to increase array size by less than 1 byte" );
|
||||
|
||||
new_array =
|
||||
(void *) realloc( (char *)array, (unsigned) (size * element_size ));
|
||||
realloc( array, (size_t) size * element_size );
|
||||
|
||||
if ( new_array == NULL )
|
||||
flexfatal( "attempt to increase array size failed" );
|
||||
|
||||
@@ -299,7 +299,7 @@ void expand_nxt_chk(void)
|
||||
nxt = reallocate_integer_array( nxt, current_max_xpairs );
|
||||
chk = reallocate_integer_array( chk, current_max_xpairs );
|
||||
|
||||
memset( (char *) (chk + old_max), 0,
|
||||
memset( chk + old_max, 0,
|
||||
MAX_XPAIRS_INCREMENT * sizeof( int ) / sizeof( char ) );
|
||||
}
|
||||
|
||||
@@ -423,7 +423,7 @@ void inittbl(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset( (char *) chk, 0,
|
||||
memset( chk, 0,
|
||||
current_max_xpairs * sizeof( int ) / sizeof( char ) );
|
||||
|
||||
tblend = 0;
|
||||
@@ -500,7 +500,7 @@ void mkdeftbl(void)
|
||||
* (i.e., jam entries) into the table. It is assumed that by linking to
|
||||
* "JAMSTATE" they will be taken care of. In any case, entries in "state"
|
||||
* marking transitions to "SAME_TRANS" are treated as though they will be
|
||||
* taken care of by whereever "deflink" points. "totaltrans" is the total
|
||||
* taken care of by wherever "deflink" points. "totaltrans" is the total
|
||||
* number of transitions out of the state. If it is below a certain threshold,
|
||||
* the tables are searched for an interior spot that will accommodate the
|
||||
* state array.
|
||||
|
||||
@@ -81,7 +81,7 @@ LIBCOM_API void epicsStdCall
|
||||
pfl->mallochead = NULL;
|
||||
pfl->nBlocksAvailable = 0u;
|
||||
pfl->lock = epicsMutexMustCreate();
|
||||
*ppvt = (void *)pfl;
|
||||
*ppvt = pfl;
|
||||
VALGRIND_CREATE_MEMPOOL(pfl, REDZONE, 0);
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ LIBCOM_API void * epicsStdCall freeListMalloc(void *pvt)
|
||||
* | RED | size0 ------ | RED | size1 | ... | RED |
|
||||
* | | next | ----- |
|
||||
*/
|
||||
ptemp = (void *)malloc(pfl->nmalloc*(pfl->size+REDZONE)+REDZONE);
|
||||
ptemp = malloc(pfl->nmalloc*(pfl->size+REDZONE)+REDZONE);
|
||||
if(ptemp==0) {
|
||||
epicsMutexUnlock(pfl->lock);
|
||||
return(0);
|
||||
|
||||
@@ -163,7 +163,7 @@ void epicsStdCall gphDelete(gphPvt *pgphPvt, const char *name, void *pvtid)
|
||||
if (pvtid == pgphNode->pvtid &&
|
||||
strcmp(name, pgphNode->name) == 0) {
|
||||
ellDelete(plist, (ELLNODE*)pgphNode);
|
||||
free((void *)pgphNode);
|
||||
free(pgphNode);
|
||||
break;
|
||||
}
|
||||
pgphNode = (GPHENTRY *) ellNext((ELLNODE*)pgphNode);
|
||||
|
||||
@@ -83,7 +83,7 @@ typedef enum {
|
||||
initHookAfterInitDatabase, /**< Records and locksets init (also autosave pass 1) */
|
||||
initHookAfterFinishDevSup, /**< Device support init pass 1 */
|
||||
initHookAfterScanInit, /**< Scan, AS, ProcessNotify init */
|
||||
initHookAfterInitialProcess, /**< Records with PINI = YES processsed */
|
||||
initHookAfterInitialProcess, /**< Records with PINI = YES processed */
|
||||
initHookAfterCaServerInit, /**< RSRV init */
|
||||
initHookAfterIocBuilt, /**< End of iocBuild() */
|
||||
|
||||
@@ -154,7 +154,7 @@ typedef enum {
|
||||
|
||||
/** \brief Type for application callback functions
|
||||
*
|
||||
* Application callback functions must match this typdef.
|
||||
* Application callback functions must match this typedef.
|
||||
* \param state initHook enumeration value
|
||||
*/
|
||||
typedef void (*initHookFunction)(initHookState state);
|
||||
@@ -163,9 +163,9 @@ typedef void (*initHookFunction)(initHookState state);
|
||||
*
|
||||
* Registers \p func for initHook notifications
|
||||
* \param func Pointer to application's notification function.
|
||||
* \return Always zero. (before UNRELEASED could return -1 on allocation failure)
|
||||
* \return Always zero. (before 7.0.10 could return -1 on allocation failure)
|
||||
*
|
||||
* \since UNRELEASED initHookRegister is idempotent.
|
||||
* \since 7.0.10 initHookRegister is idempotent.
|
||||
* Previously, repeated registrations would result
|
||||
* in duplicate calls to the hook function.
|
||||
*/
|
||||
|
||||
@@ -26,22 +26,12 @@
|
||||
|
||||
#define EPICS_PRIVATE_API
|
||||
|
||||
#include "epicsMath.h"
|
||||
#include "errlog.h"
|
||||
#include "macLib.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsStdlib.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "envDefs.h"
|
||||
#include "registry.h"
|
||||
// Recent readline.h uses printf in an attribute
|
||||
#define epicsStdioStdStreams
|
||||
#define epicsStdioStdPrintfEtc
|
||||
|
||||
#include "epicsReadline.h"
|
||||
#include "cantProceed.h"
|
||||
#include "iocsh.h"
|
||||
|
||||
#include "epicsReadlinePvt.h"
|
||||
|
||||
#if EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_READLINE
|
||||
# include <readline/readline.h>
|
||||
# include <readline/history.h>
|
||||
@@ -62,6 +52,19 @@ static const char *rl_basic_quote_characters;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "epicsMath.h"
|
||||
#include "errlog.h"
|
||||
#include "macLib.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsStdlib.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "envDefs.h"
|
||||
#include "registry.h"
|
||||
#include "cantProceed.h"
|
||||
#include "iocsh.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
/*
|
||||
@@ -165,7 +168,7 @@ void iocshRegisterImpl (const iocshFuncDef *piocshFuncDef,
|
||||
}
|
||||
n = (struct iocshCommand *) callocMustSucceed (1, sizeof *n,
|
||||
"iocshRegister");
|
||||
if (!registryAdd(iocshCmdID, piocshFuncDef->name, (void *)n)) {
|
||||
if (!registryAdd(iocshCmdID, piocshFuncDef->name, n)) {
|
||||
free (n);
|
||||
errlogPrintf ("iocshRegister failed to add %s\n", piocshFuncDef->name);
|
||||
return;
|
||||
@@ -643,8 +646,9 @@ struct ReadlineContext {
|
||||
if(!hist_file.empty()) {
|
||||
if(int err = read_history(hist_file.c_str())) {
|
||||
if(err!=ENOENT)
|
||||
fprintf(stderr, ERL_ERROR " %s (%d) loading '%s'\n",
|
||||
strerror(err), err, hist_file.c_str());
|
||||
fprintf(epicsGetStderr(),
|
||||
ERL_ERROR " %s (%d) loading '%s'\n",
|
||||
strerror(err), err, hist_file.c_str());
|
||||
}
|
||||
stifle_history(1024); // some limit...
|
||||
}
|
||||
@@ -658,8 +662,9 @@ struct ReadlineContext {
|
||||
#ifdef USE_READLINE
|
||||
if(!hist_file.empty()) {
|
||||
if(int err = write_history(hist_file.c_str())) {
|
||||
fprintf(stderr, ERL_ERROR " %s (%d) writing '%s'\n",
|
||||
strerror(err), err, hist_file.c_str());
|
||||
fprintf(epicsGetStderr(),
|
||||
ERL_ERROR " %s (%d) writing '%s'\n",
|
||||
strerror(err), err, hist_file.c_str());
|
||||
}
|
||||
}
|
||||
rl_readline_name = prev_rl_readline_name;
|
||||
@@ -741,7 +746,7 @@ void epicsStdCall iocshRegisterVariable (const iocshVarDef *piocshVarDef)
|
||||
if (!found) {
|
||||
n = (struct iocshVariable *) callocMustSucceed(1, sizeof *n,
|
||||
"iocshRegisterVariable");
|
||||
if (!registryAdd(iocshVarID, piocshVarDef->name, (void *)n)) {
|
||||
if (!registryAdd(iocshVarID, piocshVarDef->name, n)) {
|
||||
free(n);
|
||||
iocshTableUnlock();
|
||||
errlogPrintf("iocshRegisterVariable failed to add %s.\n",
|
||||
@@ -1101,7 +1106,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
|
||||
return -1;
|
||||
}
|
||||
|
||||
epicsThreadPrivateSet(iocshContextId, (void *) context);
|
||||
epicsThreadPrivateSet(iocshContextId, context);
|
||||
}
|
||||
MAC_HANDLE *handle = context->handle;
|
||||
|
||||
@@ -1171,7 +1176,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
|
||||
if (c == '#') {
|
||||
if ((prompt == NULL) && (commandLine == NULL))
|
||||
if (raw[icin + 1] != '-') {
|
||||
printf(ANSI_BLUE("%s") "\n", raw);
|
||||
fprintf(epicsGetStdout(), ANSI_BLUE("%s") "\n", raw);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -1198,7 +1203,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
|
||||
*/
|
||||
if ((prompt == NULL) && *line && (commandLine == NULL)) {
|
||||
if ((c != '#') || (line[icin + 1] != '-')) {
|
||||
printf(ANSI_BOLD("%s") "\n", line);
|
||||
fprintf(epicsGetStdout(), ANSI_BOLD("%s") "\n", line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1512,7 +1517,7 @@ static const iocshArg *onArgs[1] = {&onArg0};
|
||||
static const iocshFuncDef onFuncDef = {"on", 1, onArgs,
|
||||
"Change IOC shell error handling.\n"
|
||||
" continue (default) - Ignores error and continue with next commands.\n"
|
||||
" break - Return to caller without executing futher commands.\n"
|
||||
" break - Return to caller without executing further commands.\n"
|
||||
" halt - Suspend process.\n"
|
||||
" wait - stall process for <delay> seconds, then continue.\n"};
|
||||
static void onCallFunc(const iocshArgBuf *args)
|
||||
|
||||
@@ -12,23 +12,23 @@
|
||||
|
||||
/**
|
||||
* @file iocsh.h
|
||||
*
|
||||
* @brief C and C++ defintions of functions for IOC shell programming.
|
||||
*
|
||||
*
|
||||
* @brief C and C++ definitions of functions for IOC shell programming.
|
||||
*
|
||||
* @details
|
||||
* The iocsh API provides an interface for running commands in the shell
|
||||
* of the IOC, as well as registering commands and variables for use in the shell.
|
||||
* It consists of 4 functions for the former and 2 functions for the latter.
|
||||
*
|
||||
*
|
||||
* @par Command functions:
|
||||
* int iocsh (const char *pathname)@n
|
||||
* int iocshLoad (const char *pathname, const char *macros)@n
|
||||
* int iocshCmd (const char *cmd)@n
|
||||
* int iocshRun (const char *cmd, const char *macros)
|
||||
*
|
||||
* - iocsh()
|
||||
* - iocshLoad()
|
||||
* - iocshCmd()
|
||||
* - iocshRun()
|
||||
*
|
||||
* @par Registration functions:
|
||||
* void iocshRegister (const iocshFuncDef * piocshFuncDef, iocshCallFunc func)@n
|
||||
* void epicsStdCall iocshRegisterVariable (const iocshVarDef *piocshVarDef)
|
||||
* - iocshRegister()
|
||||
* - iocshRegisterVariable()
|
||||
*/
|
||||
|
||||
#ifndef INCiocshH
|
||||
@@ -49,22 +49,47 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @enum iocshArgType
|
||||
*
|
||||
* This typedef lists the values that can be used as argument data types
|
||||
* when building the piocshFuncDef parameter of iocshRegister().
|
||||
*
|
||||
* @code {.cpp}
|
||||
* static const iocshArg AsynGenericConfigArg0 = {"Port Name", iocshArgString};
|
||||
* static const iocshArg AsynGenericConfigArg1 = {"Number Devices", iocshArgInt};
|
||||
* @endcode
|
||||
* when building the first parameter of iocshRegister().
|
||||
*
|
||||
* ```
|
||||
* static const iocshArg AsynGenericConfigArg0 = {
|
||||
* // name
|
||||
* "Port Name",
|
||||
* // type
|
||||
* iocshArgString,
|
||||
* };
|
||||
* static const iocshArg AsynGenericConfigArg1 = {
|
||||
* // name
|
||||
* "Number Devices",
|
||||
* // type
|
||||
* iocshArgInt,
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
typedef enum {
|
||||
/** The argument is converted to an integer value. */
|
||||
iocshArgInt,
|
||||
/** The argument is converted to a double-precision floating point value. */
|
||||
iocshArgDouble,
|
||||
/** The argument is left as a string.
|
||||
*
|
||||
* The memory used to hold the string is "owned" by iocsh
|
||||
* and will be reused once the handler function returns.
|
||||
*/
|
||||
iocshArgString,
|
||||
/** The argument must be pdbbase. */
|
||||
iocshArgPdbbase,
|
||||
/** An arbitrary number of arguments is expected.
|
||||
*
|
||||
* Subsequent iocshArg structures will be ignored.
|
||||
*/
|
||||
iocshArgArgv,
|
||||
/** A copy of the argument will be made and a pointer to the copy will be passed to the handler.
|
||||
*
|
||||
* The called function should eventually release this copy
|
||||
* by using the pointer as an argument to free().
|
||||
*/
|
||||
iocshArgPersistentString,
|
||||
/**
|
||||
* Equivalent to iocshArgString with a hint for tab completion that the
|
||||
@@ -81,26 +106,46 @@ typedef enum {
|
||||
}iocshArgType;
|
||||
|
||||
/**
|
||||
* @union iocshArgBuf
|
||||
*
|
||||
* This union is used when building the func paramter of iocshRegister().
|
||||
* Each use should match the parameter type of the parameters of the
|
||||
* This union is used when building the func parameter of iocshRegister().
|
||||
* Each use should match the parameter iocshArgType of the parameters of the
|
||||
* function being registered
|
||||
*
|
||||
* @code {.cpp}
|
||||
*
|
||||
* ```
|
||||
* static void AsynGenericConfigCallFunc (const iocshArgBuf *args)
|
||||
* {
|
||||
* AsynGenericConfig (args[0].sval, args[1].ival);
|
||||
* }
|
||||
* @endcode
|
||||
* ```
|
||||
*/
|
||||
typedef union iocshArgBuf {
|
||||
/** The value as an integer.
|
||||
*
|
||||
* Corresponds to the @ref iocshArgInt type.
|
||||
*/
|
||||
int ival;
|
||||
/** The value as a double-precision floating point.
|
||||
*
|
||||
* Corresponds to the @ref iocshArgDouble type.
|
||||
*/
|
||||
double dval;
|
||||
/** The value as a string.
|
||||
*
|
||||
* Corresponds to the @ref iocshArgString and related types.
|
||||
*/
|
||||
char *sval;
|
||||
/** The value as an untyped pointer.
|
||||
*
|
||||
* Can be used with the @ref iocshArgPdbbase type.
|
||||
*/
|
||||
void *vval;
|
||||
/** The variadic arguments, for the @ref iocshArgArgv type. */
|
||||
struct {
|
||||
/** Number of arguments passed to the IOC shell command.
|
||||
*
|
||||
* Provides the number of elements of the `av` array.
|
||||
*/
|
||||
int ac;
|
||||
/** The arguments, as an array of strings. */
|
||||
char **av;
|
||||
}aval;
|
||||
}iocshArgBuf;
|
||||
@@ -119,67 +164,85 @@ typedef struct iocshVarDef {
|
||||
}iocshVarDef;
|
||||
|
||||
/**
|
||||
* @struct iocshArg
|
||||
*
|
||||
* This struct is used to indicate data types of function parameters
|
||||
* for iocshRegister(). The name element is used by the help command to print
|
||||
* a synopsis for the command. The type element describes the data type of
|
||||
* the argument and takes a value from iocshArgType.
|
||||
*
|
||||
* @code {.cpp}
|
||||
* static const iocshArg AsynGenericConfigArg0 = {"Port Name", iocshArgString};
|
||||
* static const iocshArg AsynGenericConfigArg1 = {"Number Devices", iocshArgInt};
|
||||
* static const iocshArg* const AsynGenericConfigArgs[]
|
||||
= { &AsynAXEConfigArg0, &AsynAXEConfigArg1 };
|
||||
|
||||
* @endcode
|
||||
* Data types of function parameters for iocshRegister().
|
||||
*
|
||||
* @par Example:
|
||||
* ```
|
||||
* static const iocshArg AsynGenericConfigArg0 = {
|
||||
* // name
|
||||
* "Port Name",
|
||||
* // type
|
||||
* iocshArgString,
|
||||
* };
|
||||
* static const iocshArg AsynGenericConfigArg1 = {
|
||||
* // name
|
||||
* "Number Devices",
|
||||
* // type
|
||||
* iocshArgInt,
|
||||
* };
|
||||
* static const iocshArg* const AsynGenericConfigArgs[] = {
|
||||
* &AsynGenericConfigArg0,
|
||||
* &AsynGenericConfigArg1,
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
typedef struct iocshArg {
|
||||
/** Used by the `help` command to print a synopsis for the command. */
|
||||
const char *name;
|
||||
/** Data type of the argument. */
|
||||
iocshArgType type;
|
||||
}iocshArg;
|
||||
|
||||
/**
|
||||
* @struct iocshFuncDef
|
||||
*
|
||||
* This struct is used with iocshRegister to define the function that
|
||||
* is being registered.
|
||||
*
|
||||
* name - the name of the command or function@n
|
||||
* nargs - the number of entries in the array of pointers to argument descriptions@n
|
||||
* arg - an array of pointers to structs of type iocshArg@n
|
||||
*
|
||||
* @code {.cpp}
|
||||
* static const iocshFuncDef AsynGenericConfigFuncDef
|
||||
* = { "AsynGenericConfig", 2, AsynGenericConfigArgs };
|
||||
* @endcode
|
||||
*
|
||||
* Used with iocshRegister() to define the function that is being registered.
|
||||
*
|
||||
* @par Example:
|
||||
* ```
|
||||
* static const iocshFuncDef AsynGenericConfigFuncDef = {
|
||||
* // name
|
||||
* "AsynGenericConfig",
|
||||
* // nargs
|
||||
* 2,
|
||||
* // arg
|
||||
* AsynGenericConfigArgs,
|
||||
* // usage
|
||||
* "Helpful message describing the command",
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
typedef struct iocshFuncDef {
|
||||
/** Name of the command or function. */
|
||||
const char *name;
|
||||
/** Number of entries in the array of pointers to argument descriptions.
|
||||
*
|
||||
* If 0, `arg` can be `NULL`.
|
||||
*/
|
||||
int nargs;
|
||||
/** Array of pointers to structs of type iocshArg.
|
||||
*
|
||||
* Can be `NULL` if `nargs` is 0.
|
||||
*/
|
||||
const iocshArg * const *arg;
|
||||
/** Text displayed when using running `help <command>`. */
|
||||
const char* usage;
|
||||
}iocshFuncDef;
|
||||
#define IOCSHFUNCDEF_HAS_USAGE
|
||||
|
||||
/**
|
||||
* @typedef
|
||||
*
|
||||
* This typedef defines a function that is used as the *piocshFuncDef
|
||||
* parameter of iocshRegister().
|
||||
*
|
||||
* @code {.cpp}
|
||||
* This typedef defines a function that is used
|
||||
* as the first parameter of iocshRegister().
|
||||
*
|
||||
* ```
|
||||
* static void AsynGenericConfigCallFunc (const iocshArgBuf *args)
|
||||
* {
|
||||
* AsynGenericConfig (args[0].sval, args[1].ival);
|
||||
* }
|
||||
*
|
||||
*
|
||||
* static void AsynGenericRegister(void)
|
||||
* {
|
||||
* iocshRegister(&AsynGenericConfigFuncDef, AsynGenericConfigCallFunc);
|
||||
* }
|
||||
* @endcode
|
||||
* ```
|
||||
*/
|
||||
typedef void (*iocshCallFunc)(const iocshArgBuf *argBuf);
|
||||
|
||||
@@ -194,11 +257,12 @@ typedef struct iocshCmdDef {
|
||||
}iocshCmdDef;
|
||||
|
||||
/**
|
||||
* @brief This function is used to register a command with the IOC shell
|
||||
*
|
||||
* @param piocshFuncDef A pointer to a data structure that describes the command and its arguments.
|
||||
* @param func A pointer to a function which is called by iocsh() when the command is encountered.
|
||||
* @return void
|
||||
* @brief Register a command with the IOC shell.
|
||||
*
|
||||
* @param[in] piocshFuncDef
|
||||
* A pointer to a data structure that describes the command and its arguments.
|
||||
* See the IOC Shell section of the Application Developer's Guide for more information.
|
||||
* @param[in] func A pointer to a function which is called by iocsh() when the command is encountered.
|
||||
*/
|
||||
LIBCOM_API void epicsStdCall iocshRegister(
|
||||
const iocshFuncDef *piocshFuncDef, iocshCallFunc func);
|
||||
@@ -243,46 +307,73 @@ LIBCOM_API const iocshVarDef * epicsStdCall iocshFindVariable(
|
||||
*/
|
||||
LIBCOM_API void epicsStdCall iocshFree(void);
|
||||
|
||||
/**
|
||||
* @brief This function is used to execute IOC shell commands from a file.
|
||||
*
|
||||
* Commands are read from the file until and exit command is encountered or the
|
||||
* end-of-file character is reached.
|
||||
*
|
||||
* @param pathname A string that represents the path to a file from which commands are read.
|
||||
* @return 0 on success, non-zero on error
|
||||
*
|
||||
/**
|
||||
* @brief Read and evaluate IOC shell commands from the given file.
|
||||
*
|
||||
* Equivalent to:
|
||||
* @code iocshLoad(pathname, NULL) @endcode */
|
||||
* @code iocshLoad(pathname, NULL) @endcode
|
||||
*
|
||||
* @see iocshLoad()
|
||||
*/
|
||||
LIBCOM_API int epicsStdCall iocsh(const char *pathname);
|
||||
|
||||
/**
|
||||
* @brief This function is used to exectute a single IOC shell command.
|
||||
*
|
||||
* @param cmd A string that represents the command to be executed.
|
||||
* @return 0 on success, non-zero on error
|
||||
*
|
||||
* @brief Run a single IOC shell command.
|
||||
*
|
||||
* Equivalent to:
|
||||
* @code iocshRun(cmd, NULL) @endcode */
|
||||
* @code iocshRun(cmd, NULL) @endcode
|
||||
*
|
||||
* @see iocshRun()
|
||||
*/
|
||||
LIBCOM_API int epicsStdCall iocshCmd(const char *cmd);
|
||||
/**
|
||||
* @brief Read and evaluate IOC shell commands from the given file. A list of macros
|
||||
* can be supplied as a parameter. These macros are treated as environment variables during
|
||||
* exectution of the file's commands.
|
||||
*
|
||||
* @param pathname A string that represents the path to a file from which commands are read.
|
||||
* @param macros NULL or a comma separated list of macro definitions. eg. "VAR1=x,VAR2=y"
|
||||
* @return 0 on success, non-zero on error
|
||||
|
||||
/**
|
||||
* @brief Read and evaluate IOC shell commands from the given file.
|
||||
*
|
||||
* Commands are read from the file
|
||||
* until an `exit` command is encountered
|
||||
* or end-of-file is reached.
|
||||
*
|
||||
* A list of macros can be supplied as a parameter.
|
||||
* These macros are treated as environment variables
|
||||
* during execution of the file's commands.
|
||||
*
|
||||
* @sa iocsh()
|
||||
*
|
||||
* @param[in] pathname
|
||||
* A string that represents the path to a file from which commands are read.
|
||||
* If `NULL`, commands are read from the standard input.
|
||||
* @param[in] macros
|
||||
* `NULL` or a comma separated list of macro definitions.
|
||||
* eg. `"VAR1=x,VAR2=y"`
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval non-zero on error
|
||||
* @retval -1 if the specified file can't be opened
|
||||
*/
|
||||
LIBCOM_API int epicsStdCall iocshLoad(const char *pathname, const char* macros);
|
||||
/**
|
||||
* @brief Evaluate a single IOC shell command. A list of macros can be supplied
|
||||
* as a parameter. These macros are treated as environment variables during
|
||||
* exectution of the command.
|
||||
*
|
||||
* @param cmd Command string. eg. "echo \"something or other\""
|
||||
* @param macros NULL or a comma separated list of macro definitions. eg. "VAR1=x,VAR2=y"
|
||||
* @return 0 on success, non-zero on error
|
||||
|
||||
/**
|
||||
* @brief Run a single IOC shell command.
|
||||
*
|
||||
* A list of macros can be supplied as a parameter.
|
||||
* These macros are treated as environment variables
|
||||
* during execution of the command.
|
||||
*
|
||||
* This function may be run from any thread,
|
||||
* but many IOC shell commands may not be thread-safe.
|
||||
*
|
||||
* @sa iocshCmd()
|
||||
*
|
||||
* @param[in] cmd
|
||||
* Command string.
|
||||
* eg. `"echo \"something or other\""`
|
||||
* @param[in] macros
|
||||
* `NULL` or a comma separated list of macro definitions.
|
||||
* eg. `"VAR1=x,VAR2=y"`
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval non-zero on error
|
||||
*/
|
||||
LIBCOM_API int epicsStdCall iocshRun(const char *cmd, const char* macros);
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ static void registryDumpCallFunc(const iocshArgBuf *args)
|
||||
static const iocshFuncDef iocLogInitFuncDef = {"iocLogInit",0,0,
|
||||
"Initialize IOC logging\n"
|
||||
" * EPICS environment variable 'EPICS_IOC_LOG_INET' has to be defined\n"
|
||||
" * Logging controled via 'iocLogDisable' variable\n"
|
||||
" * Logging controlled via 'iocLogDisable' variable\n"
|
||||
" see 'setIocLogDisable' command\n"};
|
||||
static void iocLogInitCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
|
||||
@@ -111,7 +111,7 @@ int main(void)
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
|
||||
pserver->pfdctx = (void *) fdmgr_init();
|
||||
pserver->pfdctx = fdmgr_init();
|
||||
if (!pserver->pfdctx) {
|
||||
fprintf(stderr, "iocLogServer: %s\n", strerror(errno));
|
||||
free(pserver);
|
||||
@@ -134,7 +134,7 @@ int main(void)
|
||||
epicsSocketEnableAddressReuseDuringTimeWaitState ( pserver->sock );
|
||||
|
||||
/* Zero the sock_addr structure */
|
||||
memset((void *)&serverAddr, 0, sizeof serverAddr);
|
||||
memset(&serverAddr, 0, sizeof serverAddr);
|
||||
serverAddr.sin_family = AF_INET;
|
||||
serverAddr.sin_port = htons(ioc_log_port);
|
||||
|
||||
@@ -670,7 +670,7 @@ static void writeMessagesToLog (struct iocLogClient *pclient)
|
||||
}
|
||||
else {
|
||||
if (status != ntci) {
|
||||
fprintf(stderr, "iocLogServer: didnt calculate number of characters correctly?\n");
|
||||
fprintf(stderr, "iocLogServer: didn't calculate number of characters correctly?\n");
|
||||
}
|
||||
pclient->pserver->filePos += status;
|
||||
}
|
||||
|
||||
@@ -503,7 +503,7 @@ logClientId epicsStdCall logClientCreate (
|
||||
pClient->shutdown = 0;
|
||||
pClient->shutdownConfirm = 0;
|
||||
|
||||
epicsAtExit (logClientDestroy, (void*) pClient);
|
||||
epicsAtExit (logClientDestroy, pClient);
|
||||
|
||||
pClient->stateChangeNotify = epicsEventCreate (epicsEventEmpty);
|
||||
if ( ! pClient->stateChangeNotify ) {
|
||||
@@ -533,7 +533,7 @@ logClientId epicsStdCall logClientCreate (
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (void *) pClient;
|
||||
return pClient;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -71,7 +71,7 @@ epicsStdCall macCreateHandle(
|
||||
/**
|
||||
* \brief Disable or enable warning messages.
|
||||
*
|
||||
* The macExpandString() routine prints warnings when it cant expand a macro.
|
||||
* The macExpandString() routine prints warnings when it can't expand a macro.
|
||||
* This routine can be used to silence those warnings. A non zero value will
|
||||
* suppress the warning messages from subsequent library routines given the
|
||||
* same \c handle.
|
||||
|
||||
@@ -28,7 +28,7 @@ a) long macCreateHandle( MAC_HANDLE **handle, char *pairs[] );
|
||||
routine.
|
||||
|
||||
macSuppressWarning can be called to suppress the marning message
|
||||
when macExpandString cant expand a macro. A non zero value will
|
||||
when macExpandString can't expand a macro. A non zero value will
|
||||
suppress the messages.
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ a) macParseDefns( MAC_HANDLE *handle, char *defns, char **pairs[] );
|
||||
significant within values but ignored elsewhere (i.e. surrounding "="
|
||||
and "," characters).
|
||||
|
||||
Probably noone will ever want to, but the special meanings of "$",
|
||||
Probably no one will ever want to, but the special meanings of "$",
|
||||
"{", "}", "(", ")", "=" and "," can all be changed via macPutXxxx()
|
||||
calls. This routine does not have a handle argument, so they must be
|
||||
changed globally for it to use the new definitions. Should it have a
|
||||
|
||||
@@ -78,7 +78,7 @@ epicsStdCall macParseDefns(
|
||||
del[0] = FALSE;
|
||||
quote = 0;
|
||||
state = preName;
|
||||
for ( c = (const char *) defns; *c != '\0'; c++ ) {
|
||||
for ( c = defns; *c != '\0'; c++ ) {
|
||||
|
||||
/* handle quotes */
|
||||
if ( quote )
|
||||
@@ -184,7 +184,7 @@ epicsStdCall macParseDefns(
|
||||
*memCpp++ = memCp;
|
||||
|
||||
/* copy value regardless of the above */
|
||||
strncpy( memCp, (const char *) ptr[i], end[i] - ptr[i] );
|
||||
strncpy( memCp, ptr[i], end[i] - ptr[i] );
|
||||
memCp += end[i] - ptr[i];
|
||||
*memCp++ = '\0';
|
||||
}
|
||||
@@ -224,9 +224,9 @@ epicsStdCall macParseDefns(
|
||||
}
|
||||
|
||||
/* free workspace */
|
||||
free( ( void * ) ptr );
|
||||
free( ( void * ) end );
|
||||
free( ( char * ) del );
|
||||
free( ( void * ) ptr ); /* cast away const */
|
||||
free( ( void * ) end ); /* cast away const */
|
||||
free( del );
|
||||
|
||||
/* debug output */
|
||||
if ( handle != NULL && handle->debug & 1 )
|
||||
@@ -238,9 +238,9 @@ epicsStdCall macParseDefns(
|
||||
/* error exit */
|
||||
error:
|
||||
errlogPrintf( "macParseDefns: failed to allocate memory\n" );
|
||||
if ( ptr != NULL ) free( ( void * ) ptr );
|
||||
if ( end != NULL ) free( ( void * ) end );
|
||||
if ( del != NULL ) free( ( char * ) del );
|
||||
if ( ptr != NULL ) free( ( void * ) ptr ); /* cast away const */
|
||||
if ( end != NULL ) free( ( void * ) end ); /* cast away const */
|
||||
if ( del != NULL ) free( del );
|
||||
*pairs = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -20,19 +20,19 @@
|
||||
|
||||
size_t adjustToWorstCaseAlignment(size_t size)
|
||||
{
|
||||
union aline {
|
||||
/* largest primative types (so far...) */
|
||||
union align {
|
||||
/* largest primitive types (so far...) */
|
||||
double dval;
|
||||
size_t uval;
|
||||
char *ptr;
|
||||
};
|
||||
|
||||
/* assert that alignment size is a power of 2 */
|
||||
STATIC_ASSERT((sizeof(union aline) & (sizeof(union aline)-1))==0);
|
||||
STATIC_ASSERT((sizeof(union align) & (sizeof(union align)-1))==0);
|
||||
|
||||
/* round up to aligment size */
|
||||
/* round up to alignment size */
|
||||
size--;
|
||||
size |= sizeof(union aline)-1;
|
||||
size |= sizeof(union align)-1;
|
||||
size++;
|
||||
|
||||
return size;
|
||||
|
||||
@@ -27,7 +27,7 @@ LIBCOM_API void * callocMustSucceed(size_t count, size_t size, const char *msg)
|
||||
errlogPrintf("%s: callocMustSucceed(%lu, %lu) - " ERL_ERROR " calloc failed\n",
|
||||
msg, (unsigned long)count, (unsigned long)size);
|
||||
errlogPrintf("Thread %s (%p) suspending.\n",
|
||||
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
|
||||
epicsThreadGetNameSelf(), epicsThreadGetIdSelf());
|
||||
errlogFlush();
|
||||
epicsThreadSuspendSelf();
|
||||
}
|
||||
@@ -43,7 +43,7 @@ LIBCOM_API void * mallocMustSucceed(size_t size, const char *msg)
|
||||
errlogPrintf("%s: mallocMustSucceed(%lu) - " ERL_ERROR " malloc failed\n",
|
||||
msg, (unsigned long)size);
|
||||
errlogPrintf("Thread %s (%p) suspending.\n",
|
||||
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
|
||||
epicsThreadGetNameSelf(), epicsThreadGetIdSelf());
|
||||
errlogFlush();
|
||||
epicsThreadSuspendSelf();
|
||||
}
|
||||
@@ -60,7 +60,7 @@ LIBCOM_API void cantProceed(const char *msg, ...)
|
||||
va_end(pvar);
|
||||
|
||||
errlogPrintf(ANSI_RED("CRITICAL ERROR") " Thread %s (%p) can't proceed, suspending.\n",
|
||||
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
|
||||
epicsThreadGetNameSelf(), epicsThreadGetIdSelf());
|
||||
|
||||
epicsStackTrace();
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ LIBCOM_API float epicsConvertDoubleToFloat(double value);
|
||||
|
||||
/* dbConvertBase is used in dbPut and dbGet string to integer conversions.
|
||||
It defaults to 0 but is set to 10 if the EPICS_DB_CONVERT_DECIMAL_ONLY
|
||||
environment variable is set.
|
||||
environment variable is YES (case insensitive).
|
||||
*/
|
||||
LIBCOM_API extern int dbConvertBase;
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ LIBCOM_API size_t epicsStrnEscapedFromRawSize(const char *buf, size_t len);
|
||||
*/
|
||||
LIBCOM_API int epicsStrCaseCmp(const char *s1, const char *s2);
|
||||
|
||||
/** \brief Does case-insensitive comparision of two strings
|
||||
/** \brief Does case-insensitive comparison of two strings
|
||||
*
|
||||
* Implements strncmp from the C standard library, except is case insensitive
|
||||
*/
|
||||
|
||||
@@ -155,7 +155,7 @@ void ipAddrToAsciiEngine::cleanup()
|
||||
ipAddrToAsciiEnginePrivate::pEngine = 0;
|
||||
}
|
||||
|
||||
// for now its probably sufficent to allocate one
|
||||
// for now its probably sufficient to allocate one
|
||||
// DNS transaction thread for all codes sharing
|
||||
// the same process that need DNS services but we
|
||||
// leave our options open for the future
|
||||
|
||||
@@ -7,7 +7,7 @@ is like the vxWorks sysClockRateGet().
|
||||
|
||||
|
||||
The implementation should be:
|
||||
Provide a default that determines the rate emperically at int time by
|
||||
Provide a default that determines the rate empirically at int time by
|
||||
calling epicsThreadSleep for amaller and smaller delays until the actual
|
||||
delay no longer decreases.
|
||||
Each os/xxx can override the default if desired.
|
||||
|
||||
@@ -45,7 +45,11 @@
|
||||
/*
|
||||
* Enable format-string checking if possible
|
||||
*/
|
||||
#if __GNUC__ * 100 + __GNUC_MINOR__ >= 404
|
||||
#define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__gnu_printf__,f,a)))
|
||||
#else
|
||||
#define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__printf__,f,a)))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Deprecation marker
|
||||
|
||||
@@ -357,7 +357,7 @@ static long devInstallAddr (
|
||||
epicsMutexMustLock(addrListLock);
|
||||
ellDelete(&addrFree[addrType], &pRange->node);
|
||||
epicsMutexUnlock(addrListLock);
|
||||
free ((void *)pRange);
|
||||
free (pRange);
|
||||
}
|
||||
else {
|
||||
pRange->begin = base + size;
|
||||
@@ -553,7 +553,7 @@ static long devCombineAdjacentBlocks(
|
||||
pRange->begin = pBefore->begin;
|
||||
ellDelete (pRangeList, &pBefore->node);
|
||||
epicsMutexUnlock(addrListLock);
|
||||
free ((void *)pBefore);
|
||||
free (pBefore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -563,7 +563,7 @@ static long devCombineAdjacentBlocks(
|
||||
pRange->end = pAfter->end;
|
||||
ellDelete (pRangeList, &pAfter->node);
|
||||
epicsMutexUnlock(addrListLock);
|
||||
free((void *)pAfter);
|
||||
free(pAfter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
/**
|
||||
* \file devLibVME.h
|
||||
* \author Marty Kraimer, Jeff Hill
|
||||
* \brief API for accessing hardware devices, mosty over VMEbus.
|
||||
* \brief API for accessing hardware devices, mostly over VMEbus.
|
||||
*
|
||||
* API for accessing hardware devices. The original APIs here were for
|
||||
* written for use with VMEbus but additional routines were added for
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
*
|
||||
* These primitives can be safely used in a multithreaded programs on symmetric multiprocessing (SMP)
|
||||
* systems. Where possible the primitives are implemented with compiler intrinsic wrappers for architecture
|
||||
* specific instructions. Otherwise they are implemeted with OS specific functions and otherwise, when lacking
|
||||
* specific instructions. Otherwise they are implemented with OS specific functions and otherwise, when lacking
|
||||
* a sufficently capable OS specific interface, then in some rare situations a mutual exclusion primitive is
|
||||
* used for synchronization.
|
||||
*
|
||||
* In operating systems environments which allow C code to run at interrupt level the implementation must
|
||||
* use interrupt level invokable CPU instruction primitives.
|
||||
* use interrupt level invocable CPU instruction primitives.
|
||||
*
|
||||
* All C++ functions are implemented in the namespace atomics which is nested inside of namespace epics.
|
||||
*/
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
#include "osdWireConfig.h"
|
||||
|
||||
#ifndef EPICS_BYTE_ORDER
|
||||
#error osdWireConfig.h didnt define EPICS_BYTE_ORDER
|
||||
#error osdWireConfig.h did not define EPICS_BYTE_ORDER
|
||||
#endif
|
||||
|
||||
#ifndef EPICS_FLOAT_WORD_ORDER
|
||||
#error osdWireConfig.h didnt define EPICS_FLOAT_WORD_ORDER
|
||||
#error osdWireConfig.h did not define EPICS_FLOAT_WORD_ORDER
|
||||
#endif
|
||||
|
||||
#endif /* INC_epicsEndian_H */
|
||||
|
||||
@@ -99,7 +99,7 @@ public:
|
||||
**/
|
||||
void wait ();
|
||||
/**\brief Wait for the event or until the specified timeout.
|
||||
* \param timeout The timeout delay in seconds. A timeout of zero is
|
||||
* \param timeout The timeout delay in seconds. A timeout of zero or less is
|
||||
* equivalent to calling tryWait(); NaN or any value too large to be
|
||||
* represented to the target OS is equivalent to no timeout.
|
||||
* \return True if the event was triggered, False if it timed out.
|
||||
@@ -189,10 +189,10 @@ LIBCOM_API epicsEventStatus epicsEventWait(
|
||||
*/
|
||||
LIBCOM_API void epicsEventMustWait(epicsEventId id);
|
||||
|
||||
/**\brief Wait an the event or until the specified timeout period is over.
|
||||
/**\brief Wait for the event or until the specified timeout period is over.
|
||||
* \note Blocks until full or timeout.
|
||||
* \param id The event identifier.
|
||||
* \param timeout The timeout delay in seconds. A timeout of zero is
|
||||
* \param timeout The timeout delay in seconds. A timeout of zero or less is
|
||||
* equivalent to calling epicsEventTryWait(); NaN or any value too large
|
||||
* to be represented to the target OS is equivalent to no timeout.
|
||||
* \return Status indicator.
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
/*
|
||||
* NOTES:
|
||||
* 1) LOG_LAST_OWNER feature is normally commented out because
|
||||
* it slows down the system at run time, anfd because its not
|
||||
* it slows down the system at run time, and because it's not
|
||||
* currently safe to convert a thread id to a thread name because
|
||||
* the thread may have exited making the thread id invalid.
|
||||
*/
|
||||
@@ -119,7 +119,7 @@ void epicsStdCall epicsMutexShow(
|
||||
epicsMutexId pmutexNode, unsigned int level)
|
||||
{
|
||||
printf("epicsMutexId %p source %s line %d\n",
|
||||
(void *)pmutexNode, pmutexNode->pFileName,
|
||||
pmutexNode, pmutexNode->pFileName,
|
||||
pmutexNode->lineno);
|
||||
if ( level > 0 ) {
|
||||
epicsMutexOsdShow(pmutexNode,level-1);
|
||||
|
||||
@@ -102,8 +102,16 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief epicsSnprintf() is meant to have the same semantics as the C99
|
||||
* function snprintf()
|
||||
* \brief Wrapper around OS snprintf()
|
||||
*
|
||||
* \param str Output buffer. Must be non-NULL.
|
||||
* \param size Capacity of output buffer, including space for nil.
|
||||
* Must be a positive value.
|
||||
* \param format Format string. Must be non-NULL.
|
||||
* \returns Non-negative value on success (see below).
|
||||
*
|
||||
* \post On success, at least a nil charactor has been written to the output buffer.
|
||||
* On failure, the output buffer state is not specified.
|
||||
*
|
||||
* \details
|
||||
* This is provided because some architectures do not implement these functions,
|
||||
@@ -124,41 +132,16 @@ extern "C" {
|
||||
* On these systems epicsSnprintf() can return an error (a value less than
|
||||
* zero) when a buffer length of zero is passed in, so callers should not use
|
||||
* that technique to calculate the length of the buffer required.
|
||||
*
|
||||
* \return The number of characters (not counting the terminating zero byte)
|
||||
* that would be written to `str` if it was large enough to hold them all; the
|
||||
* output has been truncated if the return value is `size` or more.
|
||||
*/
|
||||
LIBCOM_API int epicsStdCall epicsSnprintf(
|
||||
char *str, size_t size, EPICS_PRINTF_FMT(const char *format), ...
|
||||
) EPICS_PRINTF_STYLE(3,4);
|
||||
/**
|
||||
* \brief epicsVsnprintf() is meant to have the same semantics as the C99
|
||||
* function vsnprintf()
|
||||
* \brief vararg version of epicsSnprintf()
|
||||
*
|
||||
* \details
|
||||
* This is provided because some architectures do not implement these functions,
|
||||
* while others implement them incorrectly.
|
||||
* Standardized as a C99 function, vsnprintf() acts like vsprintf() except that
|
||||
* the `size` argument gives the maximum number of characters (including the
|
||||
* trailing zero byte) that may be placed in `str`.
|
||||
* Wrapper around OS vsnprintf().
|
||||
*
|
||||
* On some operating systems though the implementation of this function does
|
||||
* not always return the correct value. If the OS implementation does not
|
||||
* correctly return the number of characters that would have been written when
|
||||
* the output gets truncated, it is not worth trying to fix this as long as
|
||||
* they return `size-1` instead; the resulting string must always be correctly
|
||||
* terminated with a zero byte.
|
||||
*
|
||||
* In some scenarios the epicsSnprintf() implementation may not provide the
|
||||
* correct C99 semantics for the return value when `size` is given as zero.
|
||||
* On these systems epicsSnprintf() can return an error (a value less than
|
||||
* zero) when a buffer length of zero is passed in, so callers should not use
|
||||
* that technique to calculate the length of the buffer required.
|
||||
*
|
||||
* \return The number of characters (not counting the terminating zero byte)
|
||||
* that would be written to `str` if it was large enough to hold them all; the
|
||||
* output has been truncated if the return value is `size` or more.
|
||||
* \see Documentation for epicsSnprintf()
|
||||
*/
|
||||
LIBCOM_API int epicsStdCall epicsVsnprintf(
|
||||
char *str, size_t size, const char *format, va_list ap);
|
||||
|
||||
@@ -179,6 +179,15 @@ typedef struct epicsThreadOpts {
|
||||
* \param parm Passed to thread main function.
|
||||
* \param opts Modifiers for the new thread, or NULL to use target specific defaults.
|
||||
* \return NULL on error
|
||||
*
|
||||
* The newly created thread will start running immediately, and may end immediately too!
|
||||
* If joinable, the caller is responsible to arrange for a later call to epicsThreadMustJoin().
|
||||
* The returned epicsThreadId remains valid until joined.
|
||||
* For a non-joinable (detached) thread, the lifetime of the returned epicsThreadId
|
||||
* ends when that thread returns, and so it must not be used unless the caller has
|
||||
* external knowledge that the thread is still running.
|
||||
*
|
||||
* \since 7.0.2
|
||||
*/
|
||||
LIBCOM_API epicsThreadId epicsThreadCreateOpt (
|
||||
const char * name,
|
||||
@@ -197,7 +206,10 @@ LIBCOM_API epicsThreadId epicsStdCall epicsThreadMustCreate (
|
||||
|
||||
/* This gets undefined in osdThread.h on VxWorks < 6.9 */
|
||||
#define EPICS_THREAD_CAN_JOIN
|
||||
/** Wait for a joinable thread to exit (return from its main function) */
|
||||
/** Wait for a joinable thread to exit (return from its main function).
|
||||
* Thread must have been created as joinable.
|
||||
* \since 7.0.2 Test EPICS_THREAD_CAN_JOIN macro for BSP support
|
||||
*/
|
||||
LIBCOM_API void epicsThreadMustJoin(epicsThreadId id);
|
||||
/** Block the current thread until epicsThreadResume(). */
|
||||
LIBCOM_API void epicsStdCall epicsThreadSuspendSelf(void);
|
||||
|
||||
@@ -120,7 +120,7 @@ LIBCOM_API int epicsTimeGetMonotonic ( epicsTimeStamp * pDest );
|
||||
/** \name ISR-callable
|
||||
* These routines may be called from an Interrupt Service Routine, and
|
||||
* will return a value from the last current time or event time provider
|
||||
* that sucessfully returned a result from the equivalent non-ISR routine.
|
||||
* that successfully returned a result from the equivalent non-ISR routine.
|
||||
* @{
|
||||
*/
|
||||
/** \brief Get current time into \p *pDest (ISR-safe) */
|
||||
@@ -329,7 +329,7 @@ public:
|
||||
|
||||
/** \brief The default constructor sets the time to the EPICS epoch. */
|
||||
#if __cplusplus>=201103L
|
||||
constexpr epicsTime() :ts{} {}
|
||||
constexpr epicsTime() :ts{0, 0} {}
|
||||
#else
|
||||
epicsTime () {
|
||||
ts.secPastEpoch = ts.nsec = 0u;
|
||||
|
||||
@@ -47,7 +47,7 @@ void epicsThreadShowInfo(epicsThreadId pthreadInfo, unsigned int level)
|
||||
priority = param.sched_priority;
|
||||
}
|
||||
fprintf(epicsGetStdout(),"%16.16s %14p %8lu %3d%8d %8.8s%s\n",
|
||||
pthreadInfo->name,(void *)
|
||||
pthreadInfo->name,
|
||||
pthreadInfo,(unsigned long)pthreadInfo->lwpId,
|
||||
pthreadInfo->osiPriority,priority,
|
||||
pthreadInfo->isSuspended ? "SUSPEND" : "OK",
|
||||
|
||||
@@ -74,6 +74,22 @@ LIBCOM_API void epicsStdCall epicsMessageQueueDestroy(
|
||||
free(id);
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueueSend(
|
||||
epicsMessageQueueId id,
|
||||
void *message,
|
||||
unsigned int messageSize)
|
||||
{
|
||||
return mq_send(id->id, (const char*)message, messageSize, 0);
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueueReceive(
|
||||
epicsMessageQueueId id,
|
||||
void *message,
|
||||
unsigned int messageSize)
|
||||
{
|
||||
return mq_receive(id->id, (char*)message, messageSize, NULL);
|
||||
}
|
||||
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueueTrySend(
|
||||
epicsMessageQueueId id,
|
||||
|
||||
@@ -23,6 +23,4 @@ struct epicsMessageQueueOSD {
|
||||
mqd_t id;
|
||||
char name[24];
|
||||
};
|
||||
#define epicsMessageQueueSend(q,m,l) (mq_send((q)->id, (const char*)(m), (l), 0))
|
||||
#define epicsMessageQueueReceive(q,m,s) (mq_receive((q)->id, (char*)(m), (s), NULL))
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level)
|
||||
if(!status) priority = param.sched_priority;
|
||||
}
|
||||
fprintf(epicsGetStdout(),"%16.16s %14p %12lu %3d%8d %8.8s\n",
|
||||
pthreadInfo->name,(void *)
|
||||
pthreadInfo->name,
|
||||
pthreadInfo,(unsigned long)pthreadInfo->tid,
|
||||
pthreadInfo->osiPriority,priority,
|
||||
pthreadInfo->isSuspended?"SUSPEND":"OK");
|
||||
|
||||
@@ -362,7 +362,7 @@ static int rtemsDevInterruptInUseVME (unsigned vectorNumber)
|
||||
return FALSE;
|
||||
|
||||
/*
|
||||
* its a C routine. Does it match a default handler?
|
||||
* it's a C routine. Does it match a default handler?
|
||||
*/
|
||||
for (i=0; i<NELEMENTS(defaultHandlerAddr); i++) {
|
||||
if (defaultHandlerAddr[i] == psub) {
|
||||
|
||||
@@ -35,7 +35,7 @@ LIBCOM_API FILE * epicsStdCall epicsTempFile ()
|
||||
* condition where two programs end up receiving the
|
||||
* same temporary file name.
|
||||
*
|
||||
* _O_CREAT create if non-existant
|
||||
* _O_CREAT create if non-existent
|
||||
* _O_EXCL file must not exist
|
||||
* _O_RDWR read and write the file
|
||||
* _O_TEMPORARY delete file on close
|
||||
|
||||
@@ -72,7 +72,7 @@ void epicsMutexOsdShow ( struct epicsMutexParm *mutex, unsigned level )
|
||||
{
|
||||
(void)level;
|
||||
printf ("epicsMutex: win32 critical section at %p\n",
|
||||
(void * ) & mutex->osd );
|
||||
& mutex->osd );
|
||||
}
|
||||
|
||||
void epicsMutexOsdShowAll(void) {}
|
||||
|
||||
@@ -57,7 +57,7 @@ static void osiLocalAddrOnce ( void *raw )
|
||||
DWORD numifs;
|
||||
DWORD cbBytesReturned;
|
||||
|
||||
memset ( (void *) &addr, '\0', sizeof ( addr ) );
|
||||
memset ( &addr, '\0', sizeof ( addr ) );
|
||||
addr.sa.sa_family = AF_UNSPEC;
|
||||
|
||||
/* only valid for winsock 2 and above */
|
||||
@@ -114,7 +114,7 @@ static void osiLocalAddrOnce ( void *raw )
|
||||
"osiLocalAddr(): only loopback found\n");
|
||||
fail:
|
||||
/* fallback to loopback */
|
||||
memset ( (void *) &addr, '\0', sizeof ( addr ) );
|
||||
memset ( &addr, '\0', sizeof ( addr ) );
|
||||
addr.ia.sin_family = AF_INET;
|
||||
addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
osiLocalAddrResult = addr;
|
||||
@@ -124,7 +124,7 @@ fail:
|
||||
|
||||
LIBCOM_API osiSockAddr epicsStdCall osiLocalAddr (SOCKET socket)
|
||||
{
|
||||
epicsThreadOnce(&osiLocalAddrId, osiLocalAddrOnce, (void*)&socket);
|
||||
epicsThreadOnce(&osiLocalAddrId, osiLocalAddrOnce, &socket);
|
||||
return osiLocalAddrResult;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
* prototypes only appear in the windows SDK 8 and above.
|
||||
* VS2010 supplies sdk 7, but can be upgraded to later SDK
|
||||
|
||||
* To accomodate this we suuply prototypes on, for XP
|
||||
* To accommodate this we supply prototypes on, for XP
|
||||
* fall back to Tls*() which will build and run
|
||||
* correctly for epicsThreads, but means that TLS allocations from
|
||||
* epicsThreadImplicitCreate() will continue to leak (for non-EPICS threads).
|
||||
@@ -49,13 +49,13 @@
|
||||
* Also, WINE circa 5.0.3 provides the FLS storage functions, but doesn't
|
||||
* actually run the dtor function.
|
||||
*
|
||||
* we check for existance of _WIN32_WINNT_WIN8 which will only be defined
|
||||
* we check for existence of _WIN32_WINNT_WIN8 which will only be defined
|
||||
* in SDK 8 and above. If Visa is detected and SDK < 8 we will supply
|
||||
* the missing prototypes
|
||||
*/
|
||||
|
||||
#if _WIN32_WINNT >= 0x0600 /* VISTA */
|
||||
# ifdef _WIN32_WINNT_WIN8 /* Existance means using SDK 8 or higher */
|
||||
# ifdef _WIN32_WINNT_WIN8 /* Existence means using SDK 8 or higher */
|
||||
# include <fibersapi.h>
|
||||
# else
|
||||
# include <winnt.h> /* for PFLS_CALLBACK_FUNCTION */
|
||||
@@ -1042,12 +1042,12 @@ static void epicsThreadShowInfo ( epicsThreadId id, unsigned level )
|
||||
if ( pParm ) {
|
||||
unsigned long idForFormat = pParm->id;
|
||||
fprintf ( epicsGetStdout(), "%-15s %-8p %-8lx %-9u %-9s %-7s", pParm->pName,
|
||||
(void *) pParm, idForFormat, pParm->epicsPriority,
|
||||
pParm, idForFormat, pParm->epicsPriority,
|
||||
epics_GetThreadPriorityAsString ( pParm->handle ),
|
||||
epicsThreadIsSuspended ( id ) ? "suspend" : "ok" );
|
||||
if ( level ) {
|
||||
fprintf (epicsGetStdout(), " %-8p %-8p ",
|
||||
(void *) pParm->handle, (void *) pParm->parm );
|
||||
pParm->handle, pParm->parm );
|
||||
}
|
||||
if(!epicsAtomicGetIntT(&pParm->isRunning))
|
||||
fprintf (epicsGetStdout(), " ZOMBIE");
|
||||
@@ -1187,7 +1187,7 @@ LIBCOM_API void epicsStdCall epicsThreadPrivateDelete ( epicsThreadPrivateId p )
|
||||
*/
|
||||
LIBCOM_API void epicsStdCall epicsThreadPrivateSet ( epicsThreadPrivateId pPvt, void *pVal )
|
||||
{
|
||||
BOOL stat = TlsSetValue ( pPvt->key, (void *) pVal );
|
||||
BOOL stat = TlsSetValue ( pPvt->key, pVal );
|
||||
assert (stat);
|
||||
}
|
||||
|
||||
@@ -1196,7 +1196,7 @@ LIBCOM_API void epicsStdCall epicsThreadPrivateSet ( epicsThreadPrivateId pPvt,
|
||||
*/
|
||||
LIBCOM_API void * epicsStdCall epicsThreadPrivateGet ( epicsThreadPrivateId pPvt )
|
||||
{
|
||||
return ( void * ) TlsGetValue ( pPvt->key );
|
||||
return TlsGetValue ( pPvt->key );
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -86,7 +86,7 @@ private:
|
||||
bool threadHasExited;
|
||||
void updatePLL ();
|
||||
static const int pllDelay; /* integer seconds */
|
||||
// cant be static because of diff btw __stdcall and __cdecl
|
||||
// can't be static because of diff btw __stdcall and __cdecl
|
||||
friend unsigned __stdcall _pllThreadEntry ( void * pCurrentTimeIn );
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
|
||||
#include "errlog.h"
|
||||
#include "epicsExit.h"
|
||||
#include "envDefs.h"
|
||||
#include "epicsReadlinePvt.h"
|
||||
@@ -78,7 +79,7 @@ osdReadline (const char *prompt, struct readlineContext *context)
|
||||
|
||||
line = malloc(linesize);
|
||||
if (line == NULL) {
|
||||
printf("Out of memory!\n");
|
||||
fprintf(stderr, ERL_ERROR " osdReadline() Out of memory!\n");
|
||||
return NULL;
|
||||
}
|
||||
if (prompt) {
|
||||
@@ -98,7 +99,7 @@ osdReadline (const char *prompt, struct readlineContext *context)
|
||||
linesize = linelen + 50;
|
||||
cp = (char *)realloc(line, linesize);
|
||||
if (cp == NULL) {
|
||||
printf ("Out of memory!\n");
|
||||
fprintf(stderr, ERL_ERROR " osdReadline() Out of memory!\n");
|
||||
free(line);
|
||||
line = NULL;
|
||||
break;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
/* This file must be usable from both C and C++ */
|
||||
|
||||
/* if compilation fails because this wasnt found then you may need to define an OS
|
||||
/* if compilation fails because this wasn't found then you may need to define an OS
|
||||
specific osdWireConfig.h */
|
||||
#include <sys/param.h>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
# elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG
|
||||
# else
|
||||
# error EPICS hasnt been ported to run on the <sys/param.h> specified __BYTE_ORDER
|
||||
# error EPICS has not been ported to run on the <sys/param.h> specified __BYTE_ORDER
|
||||
# endif
|
||||
#else
|
||||
# ifdef BYTE_ORDER
|
||||
@@ -36,10 +36,10 @@
|
||||
# elif BYTE_ORDER == BIG_ENDIAN
|
||||
# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG
|
||||
# else
|
||||
# error EPICS hasnt been ported to run on the <sys/param.h> specified BYTE_ORDER
|
||||
# error EPICS has not been ported to run on the <sys/param.h> specified BYTE_ORDER
|
||||
# endif
|
||||
# else
|
||||
# error <sys/param.h> doesnt specify __BYTE_ORDER or BYTE_ORDER - is an OS specific osdWireConfig.h needed?
|
||||
# error <sys/param.h> does not specify __BYTE_ORDER or BYTE_ORDER - is an OS specific osdWireConfig.h needed?
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
# elif __FLOAT_WORD_ORDER == __BIG_ENDIAN
|
||||
# define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_BIG
|
||||
# else
|
||||
# error EPICS hasnt been ported to <sys/param.h> specified __FLOAT_WORD_ORDER
|
||||
# error EPICS has not been ported to <sys/param.h> specified __FLOAT_WORD_ORDER
|
||||
# endif
|
||||
#else
|
||||
# ifdef FLOAT_WORD_ORDER
|
||||
@@ -58,7 +58,7 @@
|
||||
# elif FLOAT_WORD_ORDER == BIG_ENDIAN
|
||||
# define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_BIG
|
||||
# else
|
||||
# error EPICS hasnt been ported to <sys/param.h> specified FLOAT_WORD_ORDER
|
||||
# error EPICS has not been ported to <sys/param.h> specified FLOAT_WORD_ORDER
|
||||
# endif
|
||||
# else
|
||||
/* assume that if neither __FLOAT_WORD_ORDER nor FLOAT_WORD_ORDER are
|
||||
|
||||
@@ -56,7 +56,7 @@ inline void WireGet < epicsFloat64 > (
|
||||
{
|
||||
// copy through union here
|
||||
// a) prevents over-aggressive optimization under strict aliasing rules
|
||||
// b) doesnt preclude extra copy operation being optimized away
|
||||
// b) doesn't preclude extra copy operation being optimized away
|
||||
union {
|
||||
epicsFloat64 _f;
|
||||
epicsUInt32 _u[2];
|
||||
@@ -94,7 +94,7 @@ inline void WireSet < epicsFloat64 > (
|
||||
{
|
||||
// copy through union here
|
||||
// a) prevents over-aggressive optimization under strict aliasing rules
|
||||
// b) doesnt preclude extra copy operation being optimized away
|
||||
// b) doesn't preclude extra copy operation being optimized away
|
||||
union {
|
||||
epicsFloat64 _f;
|
||||
epicsUInt32 _u[2];
|
||||
@@ -158,7 +158,7 @@ inline void AlignedWireGet < epicsFloat64 > (
|
||||
{
|
||||
// copy through union here
|
||||
// a) prevents over-aggressive optimization under strict aliasing rules
|
||||
// b) doesnt preclude extra copy operation being optimized away
|
||||
// b) doesn't preclude extra copy operation being optimized away
|
||||
union Swapper {
|
||||
epicsUInt32 _u[2];
|
||||
epicsFloat64 _f;
|
||||
@@ -214,7 +214,7 @@ inline void AlignedWireSet < epicsFloat64 > (
|
||||
{
|
||||
// copy through union here
|
||||
// a) prevents over-aggressive optimization under strict aliasing rules
|
||||
// b) doesnt preclude extra copy operation being optimized away
|
||||
// b) doesn't preclude extra copy operation being optimized away
|
||||
union Swapper {
|
||||
epicsUInt32 _u[2];
|
||||
epicsFloat64 _f;
|
||||
|
||||
@@ -41,6 +41,13 @@
|
||||
# define SOCK_CLOEXEC (0)
|
||||
#endif
|
||||
|
||||
#if defined(AI_PASSIVE) && !defined(__rtems__)
|
||||
# define USE_INFO
|
||||
#else
|
||||
# define USE_BY
|
||||
#endif
|
||||
|
||||
#ifdef USE_BY
|
||||
/*
|
||||
* Protect some routines which are not thread-safe
|
||||
*/
|
||||
@@ -60,7 +67,7 @@ static void unlockInfo (void)
|
||||
{
|
||||
epicsMutexUnlock (infoMutex);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static size_t nAttached;
|
||||
|
||||
@@ -164,6 +171,7 @@ LIBCOM_API void epicsStdCall epicsSocketDestroy ( SOCKET s )
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_BY
|
||||
/*
|
||||
* ipAddrToHostName
|
||||
* On many systems, gethostbyaddr must be protected by a
|
||||
@@ -214,6 +222,53 @@ LIBCOM_API int epicsStdCall hostToIPAddr
|
||||
unlockInfo ();
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_INFO
|
||||
|
||||
unsigned epicsStdCall ipAddrToHostName(const struct in_addr *pAddr, char *pBuf, unsigned bufSize)
|
||||
{
|
||||
osiSockAddr query;
|
||||
|
||||
if(!bufSize)
|
||||
return 0; // non-sense
|
||||
|
||||
memset(&query, 0, sizeof(query));
|
||||
query.ia.sin_family = AF_INET;
|
||||
query.ia.sin_addr = *pAddr;
|
||||
|
||||
int ret = getnameinfo(&query.sa, sizeof(query), pBuf, bufSize, NULL, 0, NI_NAMEREQD);
|
||||
if(ret==0) {
|
||||
ret = strlen (pBuf);
|
||||
|
||||
} else { // lookup fails
|
||||
ret = 0; // indicate failure to caller
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int epicsStdCall hostToIPAddr(const char *pHostName, struct in_addr *pIPA)
|
||||
{
|
||||
struct addrinfo hint, *result = NULL;
|
||||
|
||||
memset(&hint, 0, sizeof(hint));
|
||||
hint.ai_family = AF_INET;
|
||||
|
||||
int ret = getaddrinfo(pHostName, NULL, &hint, &result);
|
||||
if(ret==0) {
|
||||
const struct addrinfo *ai;
|
||||
ret = -1;
|
||||
for(ai = result; ai; ai = ai->ai_next) {
|
||||
assert(ai->ai_family==AF_INET); // ensured by hint
|
||||
const struct sockaddr_in *answer = (const struct sockaddr_in*)ai->ai_addr;
|
||||
*pIPA = answer->sin_addr;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(result) {
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsAtomic.h"
|
||||
#include "envDefs.h"
|
||||
|
||||
LIBCOM_API void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level);
|
||||
LIBCOM_API void osdThreadHooksRun(epicsThreadId id);
|
||||
@@ -93,6 +94,7 @@ static pthread_mutex_t listLock;
|
||||
static ELLLIST pthreadList = ELLLIST_INIT;
|
||||
static commonAttr *pcommonAttr = 0;
|
||||
static int epicsThreadOnceCalled = 0;
|
||||
static int wantPrioScheduling = 0;
|
||||
|
||||
static epicsThreadOSD *createImplicit(void);
|
||||
|
||||
@@ -119,7 +121,7 @@ if((status)) {\
|
||||
if(status) { \
|
||||
fprintf(stderr,"%s error %s",(message),strerror((status))); \
|
||||
fprintf(stderr," %s\n",method); \
|
||||
fprintf(stderr,"epicsThreadInit cant proceed. Program exiting\n"); \
|
||||
fprintf(stderr,"epicsThreadInit can't proceed. Program exiting\n"); \
|
||||
exit(-1);\
|
||||
}
|
||||
|
||||
@@ -384,6 +386,7 @@ static void once(void)
|
||||
checkStatusOnce(status,"pthread_attr_getschedparam");
|
||||
|
||||
findPriorityRange(pcommonAttr);
|
||||
envGetBoolConfigParam(&EPICS_ALLOW_POSIX_THREAD_PRIORITY_SCHEDULING, &wantPrioScheduling);
|
||||
|
||||
if(pcommonAttr->maxPriority == -1) {
|
||||
pcommonAttr->maxPriority = pcommonAttr->schedParam.sched_priority;
|
||||
@@ -396,18 +399,13 @@ static void once(void)
|
||||
pcommonAttr->maxPriority);
|
||||
}
|
||||
|
||||
if (errVerbose) {
|
||||
fprintf(stderr, "LRT: min priority: %d max priority %d\n",
|
||||
pcommonAttr->minPriority, pcommonAttr->maxPriority);
|
||||
}
|
||||
|
||||
#else
|
||||
if(errVerbose) fprintf(stderr,"task priorities are not implemented\n");
|
||||
#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */
|
||||
|
||||
pthreadInfo = init_threadInfo("_main_",0,epicsThreadGetStackSize(epicsThreadStackSmall),0,0,0);
|
||||
assert(pthreadInfo!=NULL);
|
||||
status = pthread_setspecific(getpthreadInfo,(void *)pthreadInfo);
|
||||
status = pthread_setspecific(getpthreadInfo,pthreadInfo);
|
||||
checkStatusOnceQuit(status,"pthread_setspecific","epicsThreadInit");
|
||||
status = mutexLock(&listLock);
|
||||
checkStatusQuit(status,"pthread_mutex_lock","epicsThreadInit");
|
||||
@@ -427,6 +425,10 @@ static void * start_routine(void *arg)
|
||||
int status;
|
||||
sigset_t blockAllSig;
|
||||
|
||||
// concurrently written from creator thread with same value
|
||||
pthreadInfo->tid = pthread_self();
|
||||
epicsAtomicWriteMemoryBarrier();
|
||||
|
||||
sigfillset(&blockAllSig);
|
||||
pthread_sigmask(SIG_SETMASK,&blockAllSig,NULL);
|
||||
status = pthread_setspecific(getpthreadInfo,arg);
|
||||
@@ -459,12 +461,16 @@ static void epicsThreadInit(void)
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
unsigned char mlocked;
|
||||
|
||||
LIBCOM_API
|
||||
void epicsThreadRealtimeLock(void)
|
||||
{
|
||||
mlocked = 0;
|
||||
#if USE_MEMLOCK
|
||||
#ifndef RTEMS_LEGACY_STACK // seems to be part of libbsd?
|
||||
if (pcommonAttr->maxPriority > pcommonAttr->minPriority) {
|
||||
if (pcommonAttr->maxPriority > pcommonAttr->minPriority && wantPrioScheduling) {
|
||||
int status = mlockall(MCL_CURRENT | MCL_FUTURE);
|
||||
|
||||
if (status) {
|
||||
@@ -473,19 +479,21 @@ void epicsThreadRealtimeLock(void)
|
||||
#ifdef __linux__
|
||||
case ENOMEM:
|
||||
fprintf(stderr, "epicsThreadRealtimeLock "
|
||||
"Warning: unable to lock memory. RLIMIT_MEMLOCK is too small or missing CAP_IPC_LOCK\n");
|
||||
ERL_WARNING ": unable to lock memory. RLIMIT_MEMLOCK is too small or missing CAP_IPC_LOCK\n");
|
||||
break;
|
||||
case EPERM:
|
||||
fprintf(stderr, "epicsThreadRealtimeLock "
|
||||
"Warning: unable to lock memory. missing CAP_IPC_LOCK\n");
|
||||
ERL_WARNING ": unable to lock memory. missing CAP_IPC_LOCK\n");
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
fprintf(stderr, "epicsThreadRealtimeLock "
|
||||
"Warning: Unable to lock the virtual address space.\n"
|
||||
ERL_WARNING ": Unable to lock the virtual address space.\n"
|
||||
"VM page faults may harm real-time performance. errno=%d\n",
|
||||
err);
|
||||
}
|
||||
} else {
|
||||
mlocked = 1;
|
||||
}
|
||||
}
|
||||
#endif // LEGACY STACK
|
||||
@@ -603,22 +611,45 @@ epicsThreadCreateOpt(const char * name,
|
||||
return 0;
|
||||
|
||||
pthreadInfo->isEpicsThread = 1;
|
||||
setSchedulingPolicy(pthreadInfo, SCHED_FIFO);
|
||||
pthreadInfo->isRealTimeScheduled = 1;
|
||||
if (wantPrioScheduling) {
|
||||
setSchedulingPolicy(pthreadInfo, SCHED_FIFO);
|
||||
pthreadInfo->isRealTimeScheduled = 1;
|
||||
}
|
||||
|
||||
/* The initial ref. will be transfered to the new thread on success,
|
||||
* but is retained on error.
|
||||
* Add a second temporary ref. for this function, in case the new thread
|
||||
* is created, then ends before pthread_create() returns!.
|
||||
*/
|
||||
epicsAtomicIncrIntT(&pthreadInfo->refcnt);
|
||||
|
||||
if (pthreadInfo->joinable) {
|
||||
/* extra ref for epicsThreadMustJoin() */
|
||||
epicsAtomicIncrIntT(&pthreadInfo->refcnt);
|
||||
}
|
||||
|
||||
status = pthread_create(&pthreadInfo->tid, &pthreadInfo->attr,
|
||||
|
||||
pthread_t new_tid;
|
||||
status = pthread_create(&new_tid, &pthreadInfo->attr,
|
||||
start_routine, pthreadInfo);
|
||||
|
||||
// pthreadInfo->tid concurrently written with same value by new thread
|
||||
pthreadInfo->tid = new_tid;
|
||||
epicsAtomicWriteMemoryBarrier();
|
||||
|
||||
free_threadInfo(pthreadInfo); // dispose of temp ref
|
||||
/* On success, pthreadInfo treat as invalid after this point.
|
||||
* (eg. very short lived thread which self-joins)
|
||||
* On error, we own all refs.
|
||||
*/
|
||||
|
||||
if (status==EPERM) {
|
||||
/* Try again without SCHED_FIFO*/
|
||||
if (pthreadInfo->joinable) {
|
||||
int cnt = epicsAtomicDecrIntT(&pthreadInfo->refcnt);
|
||||
assert(cnt==1);
|
||||
epicsAtomicDecrIntT(&pthreadInfo->refcnt); // dispose of joiner ref.
|
||||
}
|
||||
// one ref. left
|
||||
assert(1==epicsAtomicGetIntT(&pthreadInfo->refcnt));
|
||||
free_threadInfo(pthreadInfo);
|
||||
|
||||
pthreadInfo = init_threadInfo(name, opts->priority, stackSize,
|
||||
@@ -626,17 +657,28 @@ epicsThreadCreateOpt(const char * name,
|
||||
if (pthreadInfo==0)
|
||||
return 0;
|
||||
|
||||
epicsAtomicIncrIntT(&pthreadInfo->refcnt); // temp ref
|
||||
if (pthreadInfo->joinable) {
|
||||
epicsAtomicIncrIntT(&pthreadInfo->refcnt); // for caller to join
|
||||
}
|
||||
|
||||
pthreadInfo->isEpicsThread = 1;
|
||||
status = pthread_create(&pthreadInfo->tid, &pthreadInfo->attr,
|
||||
status = pthread_create(&new_tid, &pthreadInfo->attr,
|
||||
start_routine, pthreadInfo);
|
||||
|
||||
// pthreadInfo->tid concurrently written with same value by new thread
|
||||
pthreadInfo->tid = new_tid;
|
||||
epicsAtomicWriteMemoryBarrier();
|
||||
|
||||
free_threadInfo(pthreadInfo); // dispose of temp ref
|
||||
}
|
||||
checkStatusOnce(status, "pthread_create");
|
||||
if (status) {
|
||||
if (pthreadInfo->joinable) {
|
||||
/* release extra ref which would have been for epicsThreadMustJoin() */
|
||||
int cnt = epicsAtomicDecrIntT(&pthreadInfo->refcnt);
|
||||
assert(cnt==1);
|
||||
epicsAtomicDecrIntT(&pthreadInfo->refcnt); // dispose of joiner ref.
|
||||
}
|
||||
// one ref. left
|
||||
assert(1==epicsAtomicGetIntT(&pthreadInfo->refcnt));
|
||||
free_threadInfo(pthreadInfo);
|
||||
return 0;
|
||||
}
|
||||
@@ -666,6 +708,8 @@ static epicsThreadOSD *createImplicit(void)
|
||||
pthreadInfo->tid = tid;
|
||||
pthreadInfo->osiPriority = 0;
|
||||
pthreadInfo->isOkToBlock = 1;
|
||||
status = pthread_attr_init(&pthreadInfo->attr);
|
||||
checkStatusOnce(status,"pthread_attr_init");
|
||||
|
||||
#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0
|
||||
if(pthread_getschedparam(tid,&pthreadInfo->schedPolicy,&pthreadInfo->schedParam) == 0) {
|
||||
@@ -677,7 +721,7 @@ static epicsThreadOSD *createImplicit(void)
|
||||
}
|
||||
#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */
|
||||
|
||||
status = pthread_setspecific(getpthreadInfo,(void *)pthreadInfo);
|
||||
status = pthread_setspecific(getpthreadInfo,pthreadInfo);
|
||||
checkStatus(status,"pthread_setspecific createImplicit");
|
||||
if(status){
|
||||
free_threadInfo(pthreadInfo);
|
||||
@@ -980,6 +1024,11 @@ LIBCOM_API void epicsStdCall epicsThreadShowAll(unsigned int level)
|
||||
}
|
||||
status = pthread_mutex_unlock(&listLock);
|
||||
checkStatus(status,"pthread_mutex_unlock epicsThreadShowAll");
|
||||
|
||||
fprintf(stderr,
|
||||
"OSD priority range min: %d max %d, memory %slocked\n",
|
||||
pcommonAttr->minPriority, pcommonAttr->maxPriority,
|
||||
mlocked ? "" : "not ");
|
||||
}
|
||||
|
||||
LIBCOM_API void epicsStdCall epicsThreadShow(epicsThreadId showThread, unsigned int level)
|
||||
@@ -1039,7 +1088,7 @@ LIBCOM_API void epicsStdCall epicsThreadPrivateDelete(epicsThreadPrivateId id)
|
||||
assert(epicsThreadOnceCalled);
|
||||
status = pthread_key_delete(*key);
|
||||
checkStatusQuit(status,"pthread_key_delete","epicsThreadPrivateDelete");
|
||||
free((void *)key);
|
||||
free(key);
|
||||
}
|
||||
|
||||
LIBCOM_API void epicsStdCall epicsThreadPrivateSet (epicsThreadPrivateId id, void *value)
|
||||
|
||||
@@ -37,7 +37,7 @@ void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level)
|
||||
if(!status) priority = param.sched_priority;
|
||||
}
|
||||
fprintf(epicsGetStdout(),"%16.16s %14p %12lu %3d%8d %8.8s%s\n",
|
||||
pthreadInfo->name,(void *)
|
||||
pthreadInfo->name,
|
||||
pthreadInfo,(unsigned long)pthreadInfo->tid,
|
||||
pthreadInfo->osiPriority,priority,
|
||||
pthreadInfo->isSuspended?"SUSPEND":"OK",
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
#elif defined ( _BIG_ENDIAN )
|
||||
# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG
|
||||
#else
|
||||
# error EPICS hasnt been ported to byte order specified by <sys/isa_defs.h> on Solaris
|
||||
# error EPICS has not been ported to byte order specified by <sys/isa_defs.h> on Solaris
|
||||
#endif
|
||||
|
||||
/* for now, assume that Solaris doesnt run on weird arch like ARM NWFP */
|
||||
/* for now, assume that Solaris doesn't run on weird arch like ARM NWFP */
|
||||
#define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER
|
||||
|
||||
#endif /* ifdef osdWireConfig_h */
|
||||
|
||||
@@ -171,7 +171,7 @@ static long vxDevConnectInterruptVME (
|
||||
return S_dev_vectorInUse;
|
||||
}
|
||||
status = intConnect(
|
||||
(void *)INUM_TO_IVEC(vectorNumber),
|
||||
INUM_TO_IVEC(vectorNumber),
|
||||
pFunction,
|
||||
(int) parameter);
|
||||
if (status<0) {
|
||||
@@ -214,7 +214,7 @@ static long vxDevDisconnectInterruptVME (
|
||||
}
|
||||
|
||||
status = intConnect(
|
||||
(void *)INUM_TO_IVEC(vectorNumber),
|
||||
INUM_TO_IVEC(vectorNumber),
|
||||
unsolicitedHandlerEPICS,
|
||||
(int) vectorNumber);
|
||||
if(status<0){
|
||||
@@ -406,7 +406,7 @@ static int vxDevInterruptInUseVME (unsigned vectorNumber)
|
||||
psub = isrFetch (vectorNumber);
|
||||
|
||||
/*
|
||||
* its a C routine. Does it match a default handler?
|
||||
* it's a C routine. Does it match a default handler?
|
||||
*/
|
||||
for (i=0; i<NELEMENTS(defaultHandlerAddr); i++) {
|
||||
if (defaultHandlerAddr[i] == psub) {
|
||||
|
||||
@@ -87,8 +87,8 @@ EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void)
|
||||
* is the same as UINT_MAX then sizeof ( atomic_t )
|
||||
* will be the same as sizeof ( size_t )
|
||||
*
|
||||
* if ULONG_MAX != UINT_MAX then its 64 bit vxWorks and
|
||||
* WRS doesnt not supply at this time the atomic interface
|
||||
* if ULONG_MAX != UINT_MAX then it's 64 bit vxWorks and
|
||||
* WRS does not supply at this time the atomic interface
|
||||
* for 8 byte integers that is needed - so that architecture
|
||||
* receives the lock synchronized version
|
||||
*/
|
||||
@@ -153,7 +153,7 @@ EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta
|
||||
#else /* ULONG_MAX == UINT_MAX */
|
||||
|
||||
/*
|
||||
* if its 64 bit SMP vxWorks and the compiler doesnt
|
||||
* if it's 64 bit SMP vxWorks and the compiler doesn't
|
||||
* have an intrinsic then maybe there isn't any way to
|
||||
* implement these without using a global lock because
|
||||
* size_t is maybe bigger than atomic_t
|
||||
|
||||
@@ -51,7 +51,7 @@ osdReadlineBegin(struct readlineContext *context)
|
||||
if (osd->ledId == (LED_ID) ERROR) {
|
||||
context->in = stdin;
|
||||
printf("Warning -- Unabled to allocate space for command-line history.\n");
|
||||
printf("Warning -- Command-line editting disabled.\n");
|
||||
printf("Warning -- Command-line editing disabled.\n");
|
||||
}
|
||||
}
|
||||
context->osd = osd;
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
#elif _BYTE_ORDER == _BIG_ENDIAN
|
||||
# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG
|
||||
#else
|
||||
# error EPICS hasnt been ported to _BYTE_ORDER specified by vxWorks <types/vxArch.h>
|
||||
# error EPICS has not been ported to _BYTE_ORDER specified by vxWorks <types/vxArch.h>
|
||||
#endif
|
||||
|
||||
/* for now, assume that vxWorks doesnt run on weird arch like ARM NWFP */
|
||||
/* for now, assume that vxWorks doesn't run on weird arch like ARM NWFP */
|
||||
#define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER
|
||||
|
||||
#endif /* ifdef osdWireConfig_h */
|
||||
|
||||
@@ -70,7 +70,7 @@ LIBCOM_API void epicsStdCall osiSockDiscoverBroadcastAddresses
|
||||
ifa->ifa_name));
|
||||
|
||||
/*
|
||||
* If its not an internet interface then don't use it
|
||||
* If it's not an internet interface then don't use it
|
||||
*/
|
||||
if ( ifa->ifa_addr->sa_family != AF_INET ) {
|
||||
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): interface \"%s\" was not AF_INET\n", ifa->ifa_name) );
|
||||
@@ -88,7 +88,7 @@ LIBCOM_API void epicsStdCall osiSockDiscoverBroadcastAddresses
|
||||
if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) {
|
||||
struct sockaddr_in *pInetAddr = (struct sockaddr_in *) ifa->ifa_addr;
|
||||
if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
|
||||
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didnt match\n", ifa->ifa_name) );
|
||||
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didn't match\n", ifa->ifa_name) );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -207,7 +207,7 @@ static void osiLocalAddrOnce (void *raw)
|
||||
"osiLocalAddr(): only loopback found\n");
|
||||
/* fallback to loopback */
|
||||
osiSockAddr addr;
|
||||
memset ( (void *) &addr, '\0', sizeof ( addr ) );
|
||||
memset ( &addr, '\0', sizeof ( addr ) );
|
||||
addr.ia.sin_family = AF_INET;
|
||||
addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
osiLocalAddrResult = addr;
|
||||
|
||||
@@ -135,7 +135,7 @@ LIBCOM_API void epicsStdCall osiSockDiscoverBroadcastAddresses
|
||||
(unsigned)current_ifreqsize));
|
||||
|
||||
/*
|
||||
* If its not an internet interface then don't use it
|
||||
* If it's not an internet interface then don't use it
|
||||
*/
|
||||
if ( pIfreqList->ifr_addr.sa_family != AF_INET ) {
|
||||
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): interface \"%s\" was not AF_INET\n", pIfreqList->ifr_name) );
|
||||
@@ -153,7 +153,7 @@ LIBCOM_API void epicsStdCall osiSockDiscoverBroadcastAddresses
|
||||
if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) {
|
||||
struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr;
|
||||
if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
|
||||
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didnt match\n", pIfreqList->ifr_name) );
|
||||
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didn't match\n", pIfreqList->ifr_name) );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -260,7 +260,7 @@ static void osiLocalAddrOnce (void *raw)
|
||||
struct ifreq *pIfreqListEnd;
|
||||
struct ifreq *pnextifreq;
|
||||
|
||||
memset ( (void *) &addr, '\0', sizeof ( addr ) );
|
||||
memset ( &addr, '\0', sizeof ( addr ) );
|
||||
addr.sa.sa_family = AF_UNSPEC;
|
||||
|
||||
pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pIfreqList) );
|
||||
@@ -336,7 +336,7 @@ static void osiLocalAddrOnce (void *raw)
|
||||
"osiLocalAddr(): only loopback found\n");
|
||||
fail:
|
||||
/* fallback to loopback */
|
||||
memset ( (void *) &addr, '\0', sizeof ( addr ) );
|
||||
memset ( &addr, '\0', sizeof ( addr ) );
|
||||
addr.ia.sin_family = AF_INET;
|
||||
addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
osiLocalAddrResult = addr;
|
||||
|
||||
@@ -60,7 +60,7 @@ static const iocshArg * const InitArgs[1] = { &InitArg0 };
|
||||
static const iocshFuncDef InitFuncDef = {
|
||||
"ClockTime_Init", 1, InitArgs,
|
||||
"Starts or stops the IOC periodically synchronizing the OS clock\n"
|
||||
"with the higest priority working time provider.\n"};
|
||||
"with the highest priority working time provider.\n"};
|
||||
static void InitCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
ClockTime_Init(args[0].ival);
|
||||
|
||||
@@ -292,7 +292,7 @@ int NTPTime_Report(int level)
|
||||
|
||||
epicsTimeToStrftime(lastSync, sizeof(lastSync),
|
||||
"%Y-%m-%d %H:%M:%S.%06f", &NTPTimePvt.syncTime);
|
||||
printf("Syncronization interval = %.1f seconds\n",
|
||||
printf("Synchronization interval = %.1f seconds\n",
|
||||
NTPTimeSyncInterval);
|
||||
printf("Last synchronized at %s\n",
|
||||
lastSync);
|
||||
|
||||
@@ -114,7 +114,7 @@ LIBCOM_API void epicsStdCall
|
||||
*
|
||||
* This enum specifies how to interrupt a blocking socket system call.
|
||||
* Fortunately, on most systems the combination of a shutdown of both directions
|
||||
* and/or a signal is sufficent to interrupt a blocking send, receive, or
|
||||
* and/or a signal is sufficient to interrupt a blocking send, receive, or
|
||||
* connect call. For odd ball systems this is stubbed out in the osi area.
|
||||
*
|
||||
*/
|
||||
@@ -235,7 +235,7 @@ LIBCOM_API unsigned epicsStdCall ipAddrToDottedIP (
|
||||
* \param[out] pBuf Pointer to a character buffer where the output string will be placed
|
||||
* \param bufSize Size of the array pointed to by pBuf
|
||||
* \return the number of character elements stored in buffer not including the
|
||||
* null termination. This will be zero if a matching host name cant be found.
|
||||
* null termination. This will be zero if a matching host name can't be found.
|
||||
*/
|
||||
LIBCOM_API unsigned epicsStdCall ipAddrToHostName (
|
||||
const struct in_addr * pAddr, char * pBuf, unsigned bufSize );
|
||||
|
||||
@@ -161,7 +161,7 @@ inline void WireGet ( const epicsUInt8 * pWireSrc, T & dst )
|
||||
{
|
||||
// copy through union here
|
||||
// a) prevents over-aggressive optimization under strict aliasing rules
|
||||
// b) doesnt preclude extra copy operation being optimized away
|
||||
// b) doesn't preclude extra copy operation being optimized away
|
||||
WireAlias < T > tmp;
|
||||
WireGet ( pWireSrc, tmp._u );
|
||||
dst = tmp._o;
|
||||
@@ -198,7 +198,7 @@ inline void WireSet ( const T & src, epicsUInt8 * pWireDst )
|
||||
{
|
||||
// copy through union here
|
||||
// a) prevents over-aggressive optimization under strict aliasing rules
|
||||
// b) doesnt preclude extra copy operation being optimized away
|
||||
// b) doesn't preclude extra copy operation being optimized away
|
||||
WireAlias < T > tmp;
|
||||
tmp._o = src;
|
||||
WireSet ( tmp._u, pWireDst );
|
||||
@@ -234,7 +234,7 @@ inline void AlignedWireGet ( const T & src, T & dst )
|
||||
{
|
||||
// copy through union here
|
||||
// a) prevents over-aggressive optimization under strict aliasing rules
|
||||
// b) doesnt preclude extra copy operation being optimized away
|
||||
// b) doesn't preclude extra copy operation being optimized away
|
||||
WireAlias < T > srcu, dstu;
|
||||
srcu._o = src;
|
||||
AlignedWireGet ( srcu._u, dstu._u );
|
||||
@@ -246,7 +246,7 @@ inline void AlignedWireSet ( const T & src, T & dst )
|
||||
{
|
||||
// copy through union here
|
||||
// a) prevents over-aggressive optimization under strict aliasing rules
|
||||
// b) doesnt preclude extra copy operation being optimized away
|
||||
// b) doesn't preclude extra copy operation being optimized away
|
||||
WireAlias < T > srcu, dstu;
|
||||
srcu._o = src;
|
||||
AlignedWireSet ( srcu._u, dstu._u );
|
||||
|
||||
@@ -52,7 +52,7 @@ LIBCOM_API epicsRingBytesId epicsStdCall epicsRingBytesCreate(int size)
|
||||
pring->nextGet = 0;
|
||||
pring->nextPut = 0;
|
||||
pring->lock = 0;
|
||||
return((void *)pring);
|
||||
return pring;
|
||||
}
|
||||
|
||||
LIBCOM_API epicsRingBytesId epicsStdCall epicsRingBytesLockedCreate(int size)
|
||||
@@ -61,14 +61,14 @@ LIBCOM_API epicsRingBytesId epicsStdCall epicsRingBytesLockedCreate(int size)
|
||||
if(!pring)
|
||||
return NULL;
|
||||
pring->lock = epicsSpinCreate();
|
||||
return((void *)pring);
|
||||
return pring;
|
||||
}
|
||||
|
||||
LIBCOM_API void epicsStdCall epicsRingBytesDelete(epicsRingBytesId id)
|
||||
{
|
||||
ringPvt *pring = (ringPvt *)id;
|
||||
if (pring->lock) epicsSpinDestroy(pring->lock);
|
||||
free((void *)pring);
|
||||
free(pring);
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsRingBytesGet(
|
||||
|
||||
@@ -112,7 +112,7 @@ static void twdTask(void *arg)
|
||||
char tName[40];
|
||||
epicsThreadGetName(pt->tid, tName, sizeof(tName));
|
||||
errlogPrintf("Thread %s (%p) suspended\n",
|
||||
tName, (void *)pt->tid);
|
||||
tName, pt->tid);
|
||||
if (pt->callback) {
|
||||
pt->callback(pt->usr);
|
||||
}
|
||||
@@ -200,7 +200,7 @@ void taskwdInsert(epicsThreadId tid, TASKWDFUNC callback, void *usr)
|
||||
epicsMutexUnlock(mLock);
|
||||
|
||||
epicsMutexMustLock(tLock);
|
||||
ellAdd(&tList, (void *)pt);
|
||||
ellAdd(&tList, &pt->node);
|
||||
epicsMutexUnlock(tLock);
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ void taskwdRemove(epicsThreadId tid)
|
||||
pt = (struct tNode *)ellFirst(&tList);
|
||||
while (pt != NULL) {
|
||||
if (tid == pt->tid) {
|
||||
ellDelete(&tList, (void *)pt);
|
||||
ellDelete(&tList, &pt->node);
|
||||
epicsMutexUnlock(tLock);
|
||||
freeNode((union twdNode *)pt);
|
||||
|
||||
@@ -240,7 +240,7 @@ void taskwdRemove(epicsThreadId tid)
|
||||
|
||||
epicsThreadGetName(tid, tName, sizeof(tName));
|
||||
errlogPrintf("taskwdRemove: Thread %s (%p) not registered!\n",
|
||||
tName, (void *)tid);
|
||||
tName, tid);
|
||||
}
|
||||
|
||||
|
||||
@@ -259,7 +259,7 @@ void taskwdMonitorAdd(const taskwdMonitor *funcs, void *usr)
|
||||
pm->usr = usr;
|
||||
|
||||
epicsMutexMustLock(mLock);
|
||||
ellAdd(&mList, (void *)pm);
|
||||
ellAdd(&mList, &pm->node);
|
||||
epicsMutexUnlock(mLock);
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ void taskwdMonitorDel(const taskwdMonitor *funcs, void *usr)
|
||||
pm = (struct mNode *)ellFirst(&mList);
|
||||
while (pm) {
|
||||
if (pm->funcs == funcs && pm->usr == usr) {
|
||||
ellDelete(&mList, (void *)pm);
|
||||
ellDelete(&mList, &pm->node);
|
||||
freeNode((union twdNode *)pm);
|
||||
epicsMutexUnlock(mLock);
|
||||
return;
|
||||
@@ -322,7 +322,7 @@ void taskwdAnyInsert(void *key, TASKWDANYFUNC callback, void *usr)
|
||||
pm->usr = pa;
|
||||
|
||||
epicsMutexMustLock(mLock);
|
||||
ellAdd(&mList, (void *)pm);
|
||||
ellAdd(&mList, &pm->node);
|
||||
epicsMutexUnlock(mLock);
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ void taskwdAnyRemove(void *key)
|
||||
if (pm->funcs == &anyFuncs) {
|
||||
pa = (struct aNode *)pm->usr;
|
||||
if (pa->key == key) {
|
||||
ellDelete(&mList, (void *)pm);
|
||||
ellDelete(&mList, &pm->node);
|
||||
freeNode((union twdNode *)pa);
|
||||
freeNode((union twdNode *)pm);
|
||||
epicsMutexUnlock(mLock);
|
||||
@@ -382,7 +382,7 @@ LIBCOM_API void taskwdShow(int level)
|
||||
epicsThreadGetName(pt->tid, tName, sizeof(tName));
|
||||
printf("%16.16s %9s %12p %12p %12p\n",
|
||||
tName, pt->suspended ? "Suspended" : "Ok ",
|
||||
(void *)pt->tid, (void *)pt->callback, pt->usr);
|
||||
pt->tid, pt->callback, pt->usr);
|
||||
pt = (struct tNode *)ellNext(&pt->node);
|
||||
}
|
||||
}
|
||||
@@ -425,6 +425,6 @@ static void freeNode(union twdNode *pn)
|
||||
VALGRIND_MEMPOOL_FREE(&fList, pn);
|
||||
VALGRIND_MEMPOOL_ALLOC(&fList, pn, sizeof(ELLNODE));
|
||||
epicsMutexMustLock(fLock);
|
||||
ellAdd(&fList, (void *)pn);
|
||||
ellAdd(&fList, &pn->t.node);
|
||||
epicsMutexUnlock(fLock);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,11 @@ void timer::start ( epicsTimerNotify & notify, const epicsTime & expire )
|
||||
void timer::privateStart ( epicsTimerNotify & notify, const epicsTime & expire )
|
||||
{
|
||||
this->pNotify = & notify;
|
||||
this->exp = expire - ( this->queue.notify.quantum () / 2.0 );
|
||||
this->exp = expire
|
||||
#ifdef TIMER_QUANTUM_BIAS
|
||||
- ( this->queue.notify.quantum () / 2.0 )
|
||||
#endif
|
||||
;
|
||||
|
||||
bool reschedualNeeded = false;
|
||||
if ( this->curState == stateActive ) {
|
||||
|
||||
@@ -35,6 +35,16 @@
|
||||
# define debugPrintf(ARGSINPAREN)
|
||||
#endif
|
||||
|
||||
/* For historical reasons, round down expiration time on vxWorks and RTEMS.
|
||||
* Targets with a fixed timer period.
|
||||
* This biasing down by half a period allows timers which expire on every tick.
|
||||
* Otherwise, the fastest timer is 2x the tick period.
|
||||
* This results in timers expiring one period earlier than requested.
|
||||
*/
|
||||
#if defined(vxWorks) || defined(__rtems__)
|
||||
# define TIMER_QUANTUM_BIAS 1
|
||||
#endif
|
||||
|
||||
template < class T > class epicsGuard;
|
||||
|
||||
class timer : public epicsTimer, public tsDLNode < timer > {
|
||||
|
||||
@@ -148,7 +148,7 @@ double timerQueue::process ( const epicsTime & currentTime )
|
||||
}
|
||||
|
||||
//
|
||||
// only restart if they didnt cancel() the timer
|
||||
// only restart if they didn't cancel() the timer
|
||||
// while the call back was running
|
||||
//
|
||||
if ( this->cancelPending ) {
|
||||
|
||||
@@ -41,7 +41,7 @@ static int yyerror(char *str)
|
||||
The FirstFlag and yyrestart jazz is the flex-flavored version of how you
|
||||
restart the scanner if you want to parse another file. Without it, the
|
||||
scanner can only be used one time. Even if you think your code will only
|
||||
be used one time, it is desireable to put in the restart stuff anyway,
|
||||
be used one time, it is desirable to put in the restart stuff anyway,
|
||||
because some day you (or your boss) will change your mind and without it
|
||||
the IOC will crash when you make your second call to your parser.
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ is
|
||||
|
||||
%ident string
|
||||
|
||||
where string is a sequence of characters begining with a double quote
|
||||
where string is a sequence of characters beginning with a double quote
|
||||
and ending with either a double quote or the next end-of-line, whichever
|
||||
comes first. The declaration will cause a #ident directive to be written
|
||||
near the start of the output file.
|
||||
|
||||
@@ -67,7 +67,7 @@ yajl_alloc(const yajl_callbacks * callbacks,
|
||||
}
|
||||
|
||||
/* copy in pointers to allocation routines */
|
||||
memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
|
||||
memcpy(&(hand->alloc), afs, sizeof(yajl_alloc_funcs));
|
||||
|
||||
hand->callbacks = callbacks;
|
||||
hand->ctx = ctx;
|
||||
|
||||
@@ -60,7 +60,7 @@ yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset((void *) b, 0, sizeof(struct yajl_buf_t));
|
||||
memset(b, 0, sizeof(struct yajl_buf_t));
|
||||
b->alloc = alloc;
|
||||
return b;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ typedef struct yajl_bytestack_t
|
||||
if (((obs).size - (obs).used) == 0) { \
|
||||
(obs).size += YAJL_BS_INC; \
|
||||
(obs).stack = (obs).yaf->realloc((obs).yaf->ctx,\
|
||||
(void *) (obs).stack, (obs).size);\
|
||||
(obs).stack, (obs).size);\
|
||||
} \
|
||||
(obs).stack[((obs).used)++] = (byte); \
|
||||
}
|
||||
|
||||
@@ -115,9 +115,9 @@ yajl_gen_alloc(const yajl_alloc_funcs * afs)
|
||||
g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t));
|
||||
if (!g) return NULL;
|
||||
|
||||
memset((void *) g, 0, sizeof(struct yajl_gen_t));
|
||||
memset(g, 0, sizeof(struct yajl_gen_t));
|
||||
/* copy in pointers to allocation routines */
|
||||
memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
|
||||
memcpy(&(g->alloc), afs, sizeof(yajl_alloc_funcs));
|
||||
|
||||
g->print = (yajl_print_t)&yajl_buf_append;
|
||||
g->ctx = yajl_buf_alloc(&(g->alloc));
|
||||
@@ -130,7 +130,7 @@ void
|
||||
yajl_gen_reset(yajl_gen g, const char * sep)
|
||||
{
|
||||
g->depth = 0;
|
||||
memset((void *) &(g->state), 0, sizeof(g->state));
|
||||
memset(&(g->state), 0, sizeof(g->state));
|
||||
if (sep != NULL) g->print(g->ctx, sep, strlen(sep));
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ yajl_lex_alloc(yajl_alloc_funcs * alloc,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset((void *) lxr, 0, sizeof(struct yajl_lexer_t));
|
||||
memset(lxr, 0, sizeof(struct yajl_lexer_t));
|
||||
lxr->buf = yajl_buf_alloc(alloc);
|
||||
lxr->allowComments = allowComments;
|
||||
lxr->validateUTF8 = validateUTF8;
|
||||
@@ -389,7 +389,7 @@ yajl_lex_string(yajl_lexer lexer, const unsigned char * jsonText,
|
||||
/* accept it, and move on */
|
||||
}
|
||||
finish_string_lex:
|
||||
/* tell our buddy, the parser, wether he needs to process this string
|
||||
/* tell our buddy, the parser, whether he needs to process this string
|
||||
* again */
|
||||
if (hasEscapes && tok == yajl_tok_string) {
|
||||
tok = yajl_tok_string_with_escapes;
|
||||
@@ -725,7 +725,7 @@ yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText,
|
||||
* - malformed comment opening (slash not followed by
|
||||
* '*' or '/') (tok_error)
|
||||
* - eof hit. (tok_eof) */
|
||||
tok = yajl_lex_comment(lexer, (const unsigned char *) jsonText,
|
||||
tok = yajl_lex_comment(lexer, jsonText,
|
||||
jsonTextLen, offset);
|
||||
if (tok == yajl_tok_comment) {
|
||||
/* "error" is silly, but that's the initial
|
||||
|
||||
@@ -34,7 +34,7 @@ extern "C" {
|
||||
yajl_status_ok,
|
||||
/** A client callback returned zero, stopping the parse. */
|
||||
yajl_status_client_canceled,
|
||||
/** An error occured during the parse. Call yajl_get_error() for
|
||||
/** An error occurred during the parse. Call yajl_get_error() for
|
||||
* more information about the encountered error. */
|
||||
yajl_status_error
|
||||
} yajl_status;
|
||||
@@ -229,7 +229,7 @@ extern "C" {
|
||||
/** Get an error string describing the state of the parse.
|
||||
*
|
||||
* If verbose is non-zero, the message will include the JSON text where
|
||||
* the error occured, along with an arrow pointing to the specific char.
|
||||
* the error occurred, along with an arrow pointing to the specific char.
|
||||
*
|
||||
* \returns A dynamically allocated string will be returned which should
|
||||
* be freed with yajl_free_error().
|
||||
@@ -246,7 +246,7 @@ extern "C" {
|
||||
*
|
||||
* In the event an error is encountered during parsing, this function
|
||||
* affords the client a way to get the offset into the most recent
|
||||
* chunk where the error occured. 0 will be returned if no error
|
||||
* chunk where the error occurred. 0 will be returned if no error
|
||||
* was encountered.
|
||||
*/
|
||||
YAJL_API size_t yajl_get_bytes_consumed(yajl_handle hand);
|
||||
|
||||
Reference in New Issue
Block a user