2017-12-09 00:21:58 +00:00
package sync
import (
"fmt"
2018-12-02 04:05:59 +00:00
"math/rand"
2017-12-09 00:21:58 +00:00
"path"
2018-12-04 18:09:58 +00:00
"sort"
2018-12-04 17:34:38 +00:00
"strings"
2017-12-17 15:10:59 +00:00
"unicode"
2017-12-09 00:21:58 +00:00
"srs.epita.fr/fic-server/libfic"
)
2018-03-09 18:07:08 +00:00
// isFullGraphic detects if some rune are not graphic one.
// This function is usefull to display warning when importing key ending with \r.
2017-12-17 15:10:59 +00:00
func isFullGraphic ( s string ) bool {
for _ , c := range s {
if ! unicode . IsGraphic ( c ) {
return false
}
}
return true
}
2018-11-16 19:46:19 +00:00
func validatorRegexp ( vre string ) ( validator_regexp * string ) {
if len ( vre ) > 0 {
validator_regexp = & vre
} else {
validator_regexp = nil
}
return
}
2018-09-24 08:00:17 +00:00
// SyncExerciceFlags reads the content of challenge.txt and import "classic" flags as Key for the given challenge.
func SyncExerciceFlags ( i Importer , exercice fic . Exercice ) ( errs [ ] string ) {
if _ , err := exercice . WipeFlags ( ) ; err != nil {
2017-12-09 00:21:58 +00:00
errs = append ( errs , err . Error ( ) )
2018-05-12 00:01:49 +00:00
} else if _ , err := exercice . WipeMCQs ( ) ; err != nil {
errs = append ( errs , err . Error ( ) )
} else if params , err := parseExerciceParams ( i , exercice . Path ) ; err != nil {
errs = append ( errs , fmt . Sprintf ( "%q: challenge.txt: %s" , path . Base ( exercice . Path ) , err ) )
} else if len ( params . Flags ) == 0 && len ( params . FlagsUCQ ) == 0 && len ( params . FlagsMCQ ) == 0 {
errs = append ( errs , fmt . Sprintf ( "%q: has no flag" , path . Base ( exercice . Path ) ) )
2017-12-09 00:21:58 +00:00
} else {
2018-12-02 18:21:07 +00:00
kmap := map [ int64 ] fic . Flag { }
2018-05-12 00:01:49 +00:00
// Import normal flags
2018-12-04 17:34:38 +00:00
flags :
2018-05-12 00:01:49 +00:00
for nline , flag := range params . Flags {
if len ( flag . Label ) == 0 {
flag . Label = "Flag"
2017-12-09 00:21:58 +00:00
}
2018-12-04 17:34:38 +00:00
if flag . Label [ 0 ] == '`' {
errs = append ( errs , fmt . Sprintf ( "%q: flag #%d: Label should not begin with `." , path . Base ( exercice . Path ) , nline + 1 ) )
flag . Label = flag . Label [ 1 : ]
}
// Concatenate array
var raw string
if f , ok := flag . Raw . ( [ ] interface { } ) ; ok {
if len ( flag . ValidatorRe ) > 0 {
errs = append ( errs , fmt . Sprintf ( "%q: flag #%d: ValidatorRe cannot be defined for this kind of flag." , path . Base ( exercice . Path ) , nline + 1 ) )
flag . ValidatorRe = ""
}
if len ( flag . Separator ) == 0 {
flag . Separator = ","
} else if len ( flag . Separator ) > 1 {
flag . Separator = string ( flag . Separator [ 0 ] )
errs = append ( errs , fmt . Sprintf ( "%q: flag #%d: separator truncated to %q" , path . Base ( exercice . Path ) , nline + 1 , flag . Separator ) )
}
2018-12-04 18:09:58 +00:00
var fitems [ ] string
2018-12-04 17:34:38 +00:00
for i , v := range f {
if g , ok := v . ( string ) ; ok {
if strings . Index ( g , flag . Separator ) != - 1 {
errs = append ( errs , fmt . Sprintf ( "%q: flag #%d: flag items cannot contain %q character as it is used as separator. Change the separator attribute for this flag." , path . Base ( exercice . Path ) , nline + 1 , flag . Separator ) )
continue flags
} else {
2018-12-04 18:09:58 +00:00
fitems = append ( fitems , g )
2018-12-04 17:34:38 +00:00
}
} else {
errs = append ( errs , fmt . Sprintf ( "%q: flag #%d, item %d has an invalid type: can only be string, is %T." , path . Base ( exercice . Path ) , nline + 1 , i , v ) )
continue flags
}
}
2018-12-04 18:09:58 +00:00
ignord := "t"
if flag . Ordered {
sort . Strings ( fitems )
ignord = "f"
}
raw = strings . Join ( fitems , flag . Separator ) + flag . Separator
flag . Label = "`" + flag . Separator + ignord + flag . Label
2018-12-04 17:34:38 +00:00
} else if f , ok := flag . Raw . ( int64 ) ; ok {
raw = fmt . Sprintf ( "%d" , f )
} else if f , ok := flag . Raw . ( string ) ; ! ok {
errs = append ( errs , fmt . Sprintf ( "%q: flag #%d has an invalid type: can only be []string or string, not %T" , path . Base ( exercice . Path ) , nline + 1 , flag . Raw ) )
continue
} else {
raw = f
}
if ! isFullGraphic ( raw ) {
2018-05-12 00:01:49 +00:00
errs = append ( errs , fmt . Sprintf ( "%q: WARNING flag #%d: non-printable characters in flag, is this really expected?" , path . Base ( exercice . Path ) , nline + 1 ) )
2017-12-09 00:21:58 +00:00
}
2018-12-04 17:34:38 +00:00
if k , err := exercice . AddRawFlag ( flag . Label , flag . Help , flag . IgnoreCase , validatorRegexp ( flag . ValidatorRe ) , [ ] byte ( raw ) , 0 ) ; err != nil {
2018-05-12 00:01:49 +00:00
errs = append ( errs , fmt . Sprintf ( "%q: error flag #%d: %s" , path . Base ( exercice . Path ) , nline + 1 , err ) )
2017-12-09 00:21:58 +00:00
continue
2018-09-07 18:53:08 +00:00
} else {
2018-12-02 18:21:07 +00:00
if flag . Id != 0 {
kmap [ flag . Id ] = k
}
// Import dependency to flag
for _ , nf := range flag . NeedFlag {
if rf , ok := kmap [ nf . Id ] ; ! ok {
errs = append ( errs , fmt . Sprintf ( "%q: error flag #%d dependency to flag id=%s: id not defined, perhaps not available at time of processing" , path . Base ( exercice . Path ) , nline + 1 , nf . Id ) )
continue
} else if err := k . AddDepend ( rf ) ; err != nil {
errs = append ( errs , fmt . Sprintf ( "%q: error flag #%d dependency to %s: %s" , path . Base ( exercice . Path ) , nline + 1 , nf . Id , err ) )
continue
}
}
2018-09-07 18:53:08 +00:00
// Import dependency to file
for _ , lf := range flag . LockedFile {
if rf , err := exercice . GetFileByFilename ( lf . Filename ) ; err != nil {
errs = append ( errs , fmt . Sprintf ( "%q: error flag #%d dependency to %s: %s" , path . Base ( exercice . Path ) , nline + 1 , lf . Filename , err ) )
continue
} else if err := rf . AddDepend ( k ) ; err != nil {
errs = append ( errs , fmt . Sprintf ( "%q: error flag #%d dependency to %s: %s" , path . Base ( exercice . Path ) , nline + 1 , lf . Filename , err ) )
continue
}
}
2017-12-09 00:21:58 +00:00
}
}
2017-12-16 02:39:57 +00:00
2018-05-12 00:01:49 +00:00
// Import UCQ flags
for nline , flag := range params . FlagsUCQ {
if len ( flag . Label ) == 0 {
flag . Label = "Flag"
2017-12-16 02:39:57 +00:00
}
2018-05-12 00:01:49 +00:00
if ! isFullGraphic ( flag . Raw ) {
errs = append ( errs , fmt . Sprintf ( "%q: WARNING flag UCQ #%d: non-printable characters in flag, is this really expected?" , path . Base ( exercice . Path ) , nline + 1 ) )
2017-12-16 02:39:57 +00:00
}
2018-12-02 22:18:32 +00:00
if k , err := exercice . AddRawFlag ( flag . Label , flag . Help , flag . IgnoreCase , validatorRegexp ( flag . ValidatorRe ) , [ ] byte ( flag . Raw ) , flag . ChoicesCost ) ; err != nil {
2018-05-12 00:01:49 +00:00
errs = append ( errs , fmt . Sprintf ( "%q: error flag UCQ #%d: %s" , path . Base ( exercice . Path ) , nline + 1 , err ) )
2017-12-16 02:39:57 +00:00
continue
2018-09-07 18:53:08 +00:00
} else {
2018-12-02 18:21:07 +00:00
// Import dependency to file
if flag . Id != 0 {
kmap [ flag . Id ] = k
}
// Import dependency to flag
for _ , nf := range flag . NeedFlag {
if rf , ok := kmap [ nf . Id ] ; ! ok {
errs = append ( errs , fmt . Sprintf ( "%q: error flag #%d dependency to flag id=%s: id not defined, perhaps not available at time of processing" , path . Base ( exercice . Path ) , nline + 1 , nf . Id ) )
continue
} else if err := k . AddDepend ( rf ) ; err != nil {
errs = append ( errs , fmt . Sprintf ( "%q: error flag #%d dependency to %s: %s" , path . Base ( exercice . Path ) , nline + 1 , nf . Id , err ) )
continue
}
}
2018-09-07 18:53:08 +00:00
// Import dependency to file
for _ , lf := range flag . LockedFile {
if rf , err := exercice . GetFileByFilename ( lf . Filename ) ; err != nil {
errs = append ( errs , fmt . Sprintf ( "%q: error flag UCQ #%d dependency to %s: %s" , path . Base ( exercice . Path ) , nline + 1 , lf . Filename , err ) )
continue
} else if err := rf . AddDepend ( k ) ; err != nil {
errs = append ( errs , fmt . Sprintf ( "%q: error flag UCQ #%d dependency to %s: %s" , path . Base ( exercice . Path ) , nline + 1 , lf . Filename , err ) )
continue
}
}
2018-11-21 03:10:22 +00:00
// Import choices
hasOne := false
2018-12-02 04:05:59 +00:00
if ! flag . NoShuffle {
rand . Shuffle ( len ( flag . Choice ) , func ( i , j int ) {
flag . Choice [ i ] , flag . Choice [ j ] = flag . Choice [ j ] , flag . Choice [ i ]
} )
}
2018-11-21 03:10:22 +00:00
for cid , choice := range flag . Choice {
if len ( choice . Label ) == 0 {
choice . Label = choice . Value
}
if _ , err := k . AddChoice ( choice . Label , choice . Value ) ; err != nil {
errs = append ( errs , fmt . Sprintf ( "%q: error in UCQ %d choice %d: %s" , path . Base ( exercice . Path ) , nline + 1 , cid , err ) )
continue
}
if choice . Value == flag . Raw {
hasOne = true
}
}
if ! hasOne {
errs = append ( errs , fmt . Sprintf ( "%q: error in UCQ %d: no valid answer defined." , path . Base ( exercice . Path ) , nline + 1 ) )
}
2017-12-16 02:39:57 +00:00
}
}
2018-05-12 00:01:49 +00:00
// Import MCQ flags
for nline , quest := range params . FlagsMCQ {
if flag , err := exercice . AddMCQ ( quest . Label ) ; err != nil {
errs = append ( errs , fmt . Sprintf ( "%q: error flag MCQ #%d: %s" , path . Base ( exercice . Path ) , nline + 1 , err ) )
2017-12-16 02:39:57 +00:00
continue
} else {
hasOne := false
2018-11-21 03:40:32 +00:00
isJustified := false
2018-12-02 04:05:59 +00:00
if ! quest . NoShuffle {
rand . Shuffle ( len ( quest . Choice ) , func ( i , j int ) {
quest . Choice [ i ] , quest . Choice [ j ] = quest . Choice [ j ] , quest . Choice [ i ]
} )
}
2018-05-12 00:01:49 +00:00
for cid , choice := range quest . Choice {
2018-11-21 03:40:32 +00:00
var val bool
2018-12-02 03:52:15 +00:00
var justify string
2018-11-21 03:40:32 +00:00
if choice . Value == nil {
val = false
} else if p , ok := choice . Value . ( bool ) ; ok {
val = p
2018-12-02 03:52:15 +00:00
if len ( choice . Help ) > 0 {
errs = append ( errs , fmt . Sprintf ( "%q: error MCQ #%d: help field has to be used only with justified mcq." , path . Base ( exercice . Path ) , nline + 1 ) )
continue
}
2018-11-21 03:40:32 +00:00
if isJustified {
errs = append ( errs , fmt . Sprintf ( "%q: error MCQ #%d: all true items has to be justified in this MCQ." , path . Base ( exercice . Path ) , nline + 1 ) )
continue
}
} else if p , ok := choice . Value . ( string ) ; ok {
val = true
2018-12-02 03:52:15 +00:00
if len ( choice . Help ) == 0 {
choice . Help = "Flag correspondant"
}
2018-11-21 03:40:32 +00:00
if hasOne && ! isJustified {
errs = append ( errs , fmt . Sprintf ( "%q: error MCQ #%d: all true items has to be justified in this MCQ." , path . Base ( exercice . Path ) , nline + 1 ) )
continue
}
isJustified = true
2018-12-02 03:52:15 +00:00
justify = p
2018-11-21 03:40:32 +00:00
} else {
errs = append ( errs , fmt . Sprintf ( "%q: error in MCQ %d choice %d: has an invalid type. Expected true, false or a string" , path . Base ( exercice . Path ) , nline + 1 , cid ) )
continue
}
2018-12-02 03:52:15 +00:00
if e , err := flag . AddEntry ( choice . Label , val ) ; err != nil {
2018-05-12 00:01:49 +00:00
errs = append ( errs , fmt . Sprintf ( "%q: error in MCQ %d choice %d: %s" , path . Base ( exercice . Path ) , nline + 1 , cid , err ) )
2017-12-17 15:10:59 +00:00
continue
2018-12-02 03:52:15 +00:00
} else if len ( justify ) > 0 {
2018-12-02 22:18:32 +00:00
if _ , err := exercice . AddRawFlag ( fmt . Sprintf ( "%%%d%%%s" , e . Id , choice . Help ) , "" , false , nil , [ ] byte ( justify ) , 0 ) ; err != nil {
2018-12-02 03:52:15 +00:00
errs = append ( errs , fmt . Sprintf ( "%q: error MCQ #%d: %s" , path . Base ( exercice . Path ) , nline + 1 , err ) )
continue
}
2017-12-17 15:10:59 +00:00
}
2018-11-21 03:40:32 +00:00
if val {
2017-12-17 15:28:31 +00:00
hasOne = true
2017-12-16 02:39:57 +00:00
}
}
if ! hasOne {
2018-05-12 00:01:49 +00:00
errs = append ( errs , fmt . Sprintf ( "%q: warning MCQ %d: no valid answer defined, is this really expected?" , path . Base ( exercice . Path ) , nline + 1 ) )
2017-12-16 02:39:57 +00:00
}
}
}
}
2018-05-12 00:01:49 +00:00
return
2017-12-16 02:39:57 +00:00
}