I'm trying to implement a check_host function based on the SPF RFC7208. It is almost ready and the thing that has left is the macro expansion detailed in section 7 (https://www.rfc-editor.org/rfc/rfc7208#section-7). I know there are readily available solutions to this but for the sake of example or practice I want to implement my own algorithm. While doing so I have the problem that www.example.com does not fit the ABNF description. I assume it is because of my wrong reasoning and that is why I'm asking for help.
This is the ABNF copied from the document:
domain-spec = macro-string domain-end
domain-end = ( "." toplabel [ "." ] ) / macro-expand
toplabel = ( *alphanum ALPHA *alphanum ) /
( 1*alphanum "-" *( alphanum / "-" ) alphanum )
alphanum = ALPHA / DIGIT
explain-string = *( macro-string / SP )
macro-string = *( macro-expand / macro-literal )
macro-expand = ( "%{" macro-letter transformers *delimiter "}" )
/ "%%" / "%_" / "%-"
macro-literal = %x21-24 / %x26-7E
; visible characters except "%"
macro-letter = "s" / "l" / "o" / "d" / "i" / "p" / "h" /
"c" / "r" / "t" / "v"
transformers = *DIGIT [ "r" ]
delimiter = "." / "-" / "+" / "," / "/" / "_" / "="
I think that applying domain-spec to www.example.com would follow this path: domain-spec -> macro-string -> macro-literal (repeated) and this will eat up the whole string and then domain-end will never match.
Where am I wrong?
Edit 1:
I think I got the answer and the original question becomes more like a request for a confirmation. The repetition in ABNF is greedy but probably allows for back off - i.e. eat less if this would lead to a match. Looking at RFC5234 (https://www.rfc-editor.org/rfc/rfc5234), though not in its entirety, I was not able to definitely spot this. Can anyone confirm?
Related
Asking for some help here, im trying to convert text to time in duration format on Google Sheets, i´ve used some basic fuctions to to breakdown text (with delimiters as d (days) h (hour) m(minute) and s(second) into values that were then baked into a time function, however for outputs over 24 hours I was unable to get it to format properly i.e. in the image below 375 hrs should show 375:00:00 or [H]:mm:ss
Any ideas here?
Sharing the doc
https://docs.google.com/spreadsheets/d/1YWHM5tPaLOulHMbfdR8CZJsER7LBceWLQrm9f8JcV9c/edit#gid=0
Try, in J12
=(G12+H12/60+I12/60/60)/24
then apply duration format
try:
=FLATTEN(INDEX(QUERY(, "select "&TEXTJOIN(",", 1,
SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(B7:B27,
"d", "*86400"), "h", "*3600"), "m", "*60"), "s", "*1"), " ", "+")))/86400, 2))
Use regexextract(), like this:
=arrayformula(
iferror(regexextract(B7:B, "(\d+)\s*h"), 0) & ":" &
iferror(regexextract(B7:B, "(\d+)\s*m"), 0) & ":" &
iferror(regexextract(B7:B, "(\d+)\s*s"), 0)
)
The formula gives text strings. To get numeric durations, use value(), like this:
=arrayformula(
iferror( 1 /
value(
iferror(regexextract(B7:B, "(\d+)\s*h"), 0) & ":" &
iferror(regexextract(B7:B, "(\d+)\s*m"), 0) & ":" &
iferror(regexextract(B7:B, "(\d+)\s*s"), 0)
)
^ -1 )
)
Format the result column as Format > Number > Duration.
In the event there are many components in the text string you are converting, it may be beneficial to use just one regextextract(), which is possible using the recently introduced lambda functions:
=arrayformula(
map(
B7:B,
lambda(
duration,
if(
len(duration),
join(
":",
iferror(
regexextract(
duration,
{ "(\d+)\s*h"; "(\d+)\s*m"; "(\d+)\s*s" }
),
0
)
),
iferror(1/0)
)
)
)
)
See this answer for an explanation of how date and time values work in spreadsheets.
I'm writing some code to parse RSS feeds but I have trouble with the Abstruse Goose RSS feed.
If you look in that feed, dates are encoded as Mon, 06 Aug 2018 00:00:00 UTC. To me, it looks like RFC 2822.
I tried to parse it using chrono's DateTime::parse_from_rfc2822, but I get ParseError(NotEnough).
let pub_date = entry.pub_date().unwrap().to_owned();
return rfc822_sanitizer::parse_from_rfc2822_with_fallback(&pub_date)
.unwrap_or_else(|e| {
panic!(
"pub_date for item {:?} (value is {:?}) can't be parsed due to error {:?}",
&entry, pub_date, e
)
})
.naive_utc();
Is there something I'm doing wrong? Do I have to hack it some way?
I use rfc822_sanitizer which does a good job at fixing bad writing errors (most of the time). I don't think it impacts the parsing ... but who knows?
The RFC2822 date/time format is very well codified in the RFC as the following format:
date-time = [ day-of-week "," ] date FWS time [CFWS]
day-of-week = ([FWS] day-name) / obs-day-of-week
day-name = "Mon" / "Tue" / "Wed" / "Thu" /
"Fri" / "Sat" / "Sun"
date = day month year
year = 4*DIGIT / obs-year
month = (FWS month-name FWS) / obs-month
month-name = "Jan" / "Feb" / "Mar" / "Apr" /
"May" / "Jun" / "Jul" / "Aug" /
"Sep" / "Oct" / "Nov" / "Dec"
day = ([FWS] 1*2DIGIT) / obs-day
time = time-of-day FWS zone
time-of-day = hour ":" minute [ ":" second ]
hour = 2DIGIT / obs-hour
minute = 2DIGIT / obs-minute
second = 2DIGIT / obs-second
zone = (( "+" / "-" ) 4DIGIT) / obs-zone
Where obs-zone is defined as follows:
obs-zone = "UT" / "GMT" / ; Universal Time
; North American UT
; offsets
"EST" / "EDT" / ; Eastern: - 5/ - 4
"CST" / "CDT" / ; Central: - 6/ - 5
"MST" / "MDT" / ; Mountain: - 7/ - 6
"PST" / "PDT" / ; Pacific: - 8/ - 7
%d65-73 / ; Military zones - "A"
%d75-90 / ; through "I" and "K"
%d97-105 / ; through "Z", both
%d107-122 ; upper and lower case
Something a lot of people get wrong when rolling their own timestamp generation library is this particular point - how to properly label an RFC2822 TZ offset. The reason UT is as it is is because UTC and UT are not exactly the same (one has additional seconds, the other has... four variants! And the RFC does not define which one is used; they're all subtly different).
RFC2822 defines the following Address Specification types:
address = mailbox / group
mailbox = name-addr / addr-spec
name-addr = [display-name] angle-addr
angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr
group = display-name ":" [mailbox-list / CFWS] ";"
[CFWS]
display-name = phrase
mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list
address-list = (address *("," address)) / obs-addr-list
I've gone over the spec several times and can find no functional or actual difference between the mailbox-list and the address-list. What is the difference?
The difference is an "address" can be a "group".
A group might be:
undisclosed-recipients: ;
for example (as seen on Bcc's, for example).
def TempConversion(ciferTemp: Double, typeDeg: String): Double = {
var celsit: Double = 0.0
var far: Double = 0.0
if (typeDeg = "c")
far = (ciferTemp - 32) / (5.0 / 9.0)
println(far + "en farhenheit")
else if (typeDeg ="f") {
celsit = (ciferTemp * (5.0 / 9.0)) + 32
println(celsit + "en celsius")}
}
I cannot figure out what is wrong with this function, it tells me a semi-colon is expected.
The problem is that you started a statement with else.
With an if block, you can only omit the {curly brackets} if there is exactly one statement. Since you put a second statement, the compiler assumes that it is not part of the if block, and therefore the else that comes after is the start of a brand new expression. Since else isn't a valid start of an expression, the compiler is confused, and gives you that error.
Since you intend have multiple lines/statements in your initial if block, you should wrap it with brackets. You should also indent both of those lines (lack of proper indentation isn't the problem here, but is still important).
(oh and you are using an assignment operator = where you should have an equality operator ==)
Here's what it ought to look like:
if(typeDeg == "c"){
far = (ciferTemp - 32) / (5.0 / 9.0)
println(far + "en farhenheit")
} else if(typeDeg == "f") {
celsit = (ciferTemp * (5.0 / 9.0)) + 32
println(celsit + "en celsius")
}
#Dylan addresses some issues with the if else and ==. Here's how you could do it with pattern matching to make it even more concise. You can add other cases to handle conversions to other units of measure and one for invalid typeDeg input.
def TempConversion(ciferTemp: Double, typeDeg: String): Double = typeDeg match {
case "c" => (ciferTemp - 32) / (5.0 / 9.0)
case "f" => (ciferTemp * (5.0 / 9.0)) + 32
}
After making some changes
def TempConversion(ciferTemp: Double, typeDeg: String): Double = {
if (typeDeg == "c") {
val far = (ciferTemp - 32) / (5.0 / 9.0)
println(far + "en farhenheit")
far
} else if (typeDeg == "f") {
val celsit = (ciferTemp * (5.0 / 9.0)) + 32
println(celsit + "en celsius")
celsit
} else {
// throw an exception, maybe?
0.0
}
}
(use == not = for comparisons, use {} around the if-else,return the result of the calculation from each part of the if/else, do something when typeDeg has neither expected value, move the declarations of far and celsit inside the relevant blocks (and change them to vals))
I am trying to find out how to do the Progress equivalent of a "GROUP BY" function. I am trying to write a procedure that will list product inventory information for multiple locations. Right now, it is listing the product information twice; once for each location. I have tried the BREAK BY functional unsuccessfully. My current & desired output and code is below:
Current Output:
Desired Output:
DEF INPUT PARAMETER ip-um AS CHARACTER NO-UNDO.
MESSAGE
"ProdCode" + "^" +
"ProdName" + "^" +
"ProdUM" + "^" +
"GrossPkgdWeight" + "^" +
"QtyOH - LOC1" + "^" +
"QtyOH - LOC2"
SKIP.
FOR EACH product-um WHERE
product-um.gross-pkgd-weight <= 0.0000
NO-LOCK,
EACH product WHERE
product.product-key = product-um.product-key AND
product.can-be-sold = YES
NO-LOCK,
EACH inventory WHERE
inventory.product-key = product.product-key AND
inventory.qoh > 0 AND
inventory.level = 2
NO-LOCK,
EACH um WHERE
um.um-key = product-um.um-key AND
um.um = ip-um
NO-LOCK
BREAK BY product.product-code:
MESSAGE
product.product-code + "^" +
product.product-name + "^" +
um.um-code + "^" +
STRING(product-um.gross-pkgd-weight) + "^" +
IF inventory.level-key-2 = '00000001' THEN STRING(inventory.qoh) ELSE "0"
+ "^" + IF inventory.level-key-2 = '00000002' THEN STRING(inventory.qoh) ELSE "0"
SKIP.
END.
because you accumulate invesntory.qoh in dependency of inventory.level-key-2 the ACCUMULATE stmt is not realy feasible so coding the accumulation manually would be the best choise
DEFINE VARIABLE loc1 AS INTEGER NO-UNDO.
DEFINE VARIABLE loc2 AS INTEGER NO-UNDO.
FOR EACH product-um NO-LOCK
WHERE product-um.gross-pkgd-weight <= 0.0000
,
EACH product NO-LOCK
WHERE product.product-key = product-um.product-key
AND product.can-be-sold = YES
,
EACH inventory NO-LOCK
WHERE inventory.product-key = product.product-key
AND inventory.product-code = product.product-code
AND inventory.qoh > 0
AND inventory.level = 2
,
EACH um NO-LOCK
WHERE um.um-key = product-um.um-key
and um.um = ip-um
BREAK
BY product.product-code:
CASE (inventory.level-key-2):
WHEN "00000001"
THEN loc1 = loc1 + inventory.qoh.
WHEN "00000002"
THEN loc2 = loc2 + inventory.qoh.
END CASE.
IF LAST-OF(product.product-code)
THEN DO:
MESSAGE
product.product-code + "^" +
product.product-name + "^" +
um.um-code + "^" +
STRING(product-um.gross-pkgd-weight) + "^" +
STRING(loc1) + "^" +
STRING(loc2)
SKIP.
ASSIGN
loc1 = 0
loc2 = 0
.
END.
END.
BREAK BY tells the compiler to mark when the FOR EACH has come to the start or end of a break group.To detect these changes you'll need to use one of these functions to detect that change: FIRST(table.field), FIRST-OF(table.field), LAST(table.field), and LAST-OF(table.field).
Once the required function returns true, you can use the ABL supplied functions like ACCUMULATE, COUNT, TOTAL, etc. to display the desired results. Personally I find the concepts a bit hard to get my head around, so I declare some local variables and do the totals that way.