open Types open Common open Printf let bpos = -1, -1 let raw_pos2pos(a, b) = !Info.current_file, a, b let raw_pos_range { pos = (a1, b1) } { pos = (a2, b2) } = (if a1 = -1 then a2 else a1), (if b2 = -1 then b1 else b2) let pos_range esp1 esp2 = raw_pos2pos (raw_pos_range esp1 esp2) let get_pos pesp = raw_pos2pos pesp.pos let get_pos_start { pos = (start, _) } = start let get_pos_end { pos = (_, end_) } = end_ let var_dollar_ pos = Deref(I_scalar, Ident(None, "_", pos)) let var_STDOUT = Deref(I_star, Ident(None, "STDOUT", raw_pos2pos bpos)) let new_any mcontext any spaces pos = { mcontext = mcontext ; any = any ; spaces = spaces ; pos = pos } let new_any_ any spaces pos = new_any M_unknown any spaces pos let new_esp mcontext e esp_start esp_end = new_any mcontext e esp_start.spaces (raw_pos_range esp_start esp_end) let new_1esp e esp = new_any esp.mcontext e esp.spaces esp.pos let new_pesp mcontext prio e esp_start esp_end = new_any mcontext { priority = prio ; expr = e } esp_start.spaces (raw_pos_range esp_start esp_end) let new_1pesp prio e esp = new_any esp.mcontext { priority = prio ; expr = e } esp.spaces esp.pos let default_esp e = new_any M_unknown e Space_none bpos let default_pesp prio e = new_any M_unknown { priority = prio ; expr = e } Space_none bpos let split_name_or_fq_name full_ident = match split_at2 ':'':' full_ident with | [] -> internal_error "split_ident" | [ident] -> None, ident | l -> let fql, name = split_last l in let fq = String.concat "::" fql in Some fq, name let is_var_dollar_ = function | Deref(I_scalar, Ident(None, "_", _)) -> true | _ -> false let is_var_number_match = function | Deref(I_scalar, Ident(None, s, _)) -> String.length s = 1 && s.[0] <> '0' && char_is_number s.[0] | _ -> false let non_scalar_context context = context = I_hash || context = I_array let is_scalar_context context = context = I_scalar let rec is_not_a_scalar = function | Deref_with(_, context, _, _) | Deref(context, _) -> non_scalar_context context | List [] | List(_ :: _ :: _) -> true | Call(Deref(I_func, Ident(None, "map", _)), _) | Call(Deref(I_func, Ident(None, "grep", _)), _) -> true | Call_op("?:", [ _cond ; a; b ], _) -> is_not_a_scalar a || is_not_a_scalar b | _ -> false let is_a_scalar = function | Ref _ | Num _ | Raw_string _ | String _ | Call(Deref(I_func, Ident(None, "N", _)), _) -> true | My_our(_, [ context, _ ], _) | Deref_with(_, context, _, _) | Deref(context, _) -> is_scalar_context context | _ -> false let is_a_string = function | String _ | Raw_string _ -> true | _ -> false let is_parenthesized = function | List[] | List[List _] -> true | _ -> false let un_parenthesize = function | List[List[e]] -> e | List[e] -> e | _ -> internal_error "un_parenthesize" let rec un_parenthesize_full = function | List[e] -> un_parenthesize_full e | e -> e let rec un_parenthesize_full_l = function | [ List l ] -> un_parenthesize_full_l l | l -> l let is_always_true = function | Num(n, _) -> float_of_string n <> 0. | Raw_string(s, _) -> s <> "" | String(l, _) -> l <> [] | Ref _ -> true | _ -> false let is_always_false = function | Num(n, _) -> float_of_string n = 0. | Raw_string(s, _) -> s = "" | String(l, _) -> l = [] | List [] -> true | Ident(None, "undef", _) -> true | _ -> false let rec is_lvalue = function | Call(Deref(I_func, Ident(None, f, _)), _) -> List.mem f [ "substr" ] | Call_op("?:", [ _ ; a ; b ], _) -> is_lvalue a && is_lvalue b | Call_op("local", l, _) | List [ List l ] -> List.for_all is_lvalue l | My_our _ | Deref(_, _) | Deref_with(_, _, _, _) | Ident(None, "undef", _) -> true | _ -> false let not_complex e = if is_parenthesized e then true else let rec not_complex_ op = function | Call_op("?:", _, _) -> false | Call_op(op', l, _) -> op <> op' && List.for_all (not_complex_ op') l | e -> not (is_parenthesized e) in not_complex_ "" (un_parenthesize_full e) let not_simple = function | Num _ | Ident _ | Deref(_, Ident _) -> false | _ -> true let context2s = function | I_scalar -> "$" | I_hash -> "%" | I_array -> "@" | I_func -> "&" | I_raw -> "" | I_star -> "*" let variable2s(context, ident) = context2s context ^ ident let rec string_of_fromparser = function | Semi_colon -> ";" | Undef -> "undef" | Num(num, _) -> num | Raw_string(s, _) -> "\"" ^ s ^ "\"" | String(l, _) -> let l' = List.map (fun (s, e) -> s ^ if e = List[] then "" else string_of_fromparser e ) l in "\"" ^ String.concat "" l' ^ "\"" | Ident(None, s, _) -> s | Ident(Some fq, s, _) -> fq ^ "::" ^ s | My_our(myour, l, _) -> myour ^ "(" ^ String.concat "," (List.map (fun (context, s) -> context2s context ^ s) l) ^ ")" | Anonymous_sub(_, e, _) -> "sub { " ^ string_of_fromparser e ^ " }" | Ref(_, e) -> "\\" ^ string_of_fromparser e | Deref(context, e) -> context2s context ^ string_of_fromparser e | Diamond(None) -> "<>" | Diamond(Some e) -> "<" ^ string_of_fromparser e ^ ">" | Sub_declaration(name, _prototype, body, Real_sub_declaration) -> "sub " ^ string_of_fromparser name ^ " { " ^ string_of_fromparser body ^ " }" | Sub_declaration(name, _prototype, body, Glob_assign) -> "*" ^ string_of_fromparser name ^ " = sub { " ^ string_of_fromparser body ^ " };" | Deref_with(_, _, _e1, _e2) -> internal_error "todo" | Package(p) -> "package " ^ string_of_fromparser p | Use(e, []) -> "use " ^ string_of_fromparser e | Use(e, l) -> "use " ^ string_of_fromparser e ^ "(" ^ lstring_of_fromparser l | List l -> lstring_of_fromparser_parentheses l | Block l -> "{ " ^ lstring_of_fromparser l ^ " }" | Call_op(op, l, _) -> op ^ lstring_of_fromparser_parentheses l | Call(e, l) -> string_of_fromparser e ^ lstring_of_fromparser l | Method_call(obj, meth, l) -> let para = if l = [] then "" else lstring_of_fromparser_parentheses l in string_of_fromparser obj ^ "->" ^ string_of_fromparser meth ^ para | Label(e) -> e ^ ": " | Perl_checker_comment _ -> "" | Too_complex -> "XXX" and lstring_of_fromparser l = String.concat ", " (List.map string_of_fromparser l) and lstring_of_fromparser_parentheses l = "(" ^ lstring_of_fromparser l ^ ")" let rec is_same_fromparser a b = match a, b with | Undef, Undef -> true | Ident(fq1, s1, _), Ident(fq2, s2, _) -> fq1 = fq2 && s1 = s2 | Num(s1, _), Num(s2, _) | Raw_string(s1, _), Raw_string(s2, _) -> s1 = s2 | String(l1, _), String(l2, _) -> for_all2_ (fun (s1, e1) (s2, e2) -> s1 = s2 && is_same_fromparser e1 e2) l1 l2 | Ref(c1, e1), Ref(c2, e2) | Deref(c1, e1), Deref(c2, e2) -> c1 = c2 && is_same_fromparser e1 e2 | Deref_with(c1, c_1, e1, e_1), Deref_with(c2, c_2, e2, e_2) -> c1 = c2 && c_1 = c_2 && is_same_fromparser e1 e2 && is_same_fromparser e_1 e_2 | Diamond(None), Diamond(None) -> true | Diamond(Some e1), Diamond(Some e2) -> is_same_fromparser e1 e2 | List(l1), List(l2) -> for_all2_ is_same_fromparser l1 l2 | Call_op(op1, l1, _), Call_op(op2, l2, _) -> op1 = op2 && for_all2_ is_same_fromparser l1 l2 | Call(e1, l1), Call(e2, l2) -> is_same_fromparser e1 e2 && for_all2_ is_same_fromparser l1 l2 | Method_call(e1, m1, l1), Method_call(e2, m2, l2) -> is_same_fromparser e1 e2 && is_same_fromparser m1 m2 && for_all2_ is_same_fromparser l1 l2 | _ -> false let from_scalar esp = match esp.any with | Deref(I_scalar, ident) -> ident | _ -> internal_error "from_scalar" let from_array esp = match esp.any with | Deref(I_array, ident) -> ident | _ -> internal_error "from_array" let rec get_pos_from_expr = function | Anonymous_sub(_, _, pos) | String(_, pos) | Call_op(_, _, pos) | Perl_checker_comment(_, pos) | My_our(_, _, pos) | Raw_string(_, pos) | Num(_, pos) | Ident(_, _, pos) -> pos | Package e | Ref(_, e) | Deref(_, e) | Sub_declaration(e, _, _, _) | Deref_with(_, _, e, _) | Use(e, _) | Call(e, _) | Method_call(_, e, _) -> get_pos_from_expr e | Diamond(option_e) -> if option_e = None then raw_pos2pos bpos else get_pos_from_expr (some option_e) | List l | Block l -> if l = [] then raw_pos2pos bpos else get_pos_from_expr (List.hd l) | Semi_colon | Too_complex | Undef | Label _ -> raw_pos2pos bpos let msg_with_rawpos (start, end_) msg = Info.pos2sfull_current start end_ ^ msg let die_with_rawpos raw_pos msg = failwith (msg_with_rawpos raw_pos msg) let warn warn_types raw_pos msg = if Flags.are_warning_types_set warn_types then print_endline_flush (msg_with_rawpos raw_pos msg) let die_rule msg = die_with_rawpos (Parsing.symbol_start(), Parsing.symbol_end()) msg let warn_rule warn_types msg = warn warn_types (Parsing.symbol_start(), Parsing.symbol_end()) msg let warn_verb warn_types pos msg = if not !Flags.quiet then warn warn_types (pos, pos) msg let warn_too_many_space start = warn_verb [Warn_white_space] start "you should have only one space here" let warn_no_space start = warn_verb [Warn_white_space] start "you should have a space here" let warn_cr start = warn_verb [Warn_white_space] start "you should not have a carriage-return (\\n) here" let warn_space start = warn_verb [Warn_white_space] start "you should not have a space here" let rec prio_less = function | P_none, _ | _, P_none -> internal_error "prio_less" | P_paren_wanted prio1, prio2 | prio1, P_paren_wanted prio2 -> prio_less(prio1, prio2) | P_ternary, P_or -> false | P_ternary, P_and -> false | _, P_loose -> true | P_loose, _ -> false | _, P_or -> true | P_or, _ -> false | _, P_and -> true | P_and, _ -> false | _, P_call_no_paren -> true | P_call_no_paren, _ -> false | _, P_comma -> true | P_comma, _ -> false | _, P_assign -> true | P_assign, _ -> false | _, P_ternary -> true | P_ternary, _ -> false | _, P_tight_or -> true | P_tight_or, _ -> false | _, P_tight_and -> true | P_tight_and, _ -> false | P_bit, P_bit -> true | P_bit, _ -> false | _, P_expr -> true | P_expr, _ -> false | _, P_eq -> true | P_eq, _ -> false | _, P_cmp -> true | P_cmp, _ -> false | _, P_add -> true | P_add, _ -> false | _, P_mul -> true | P_mul, _ -> false | _, P_tight -> true | P_tight, _ -> false | _, P_paren _ -> true | P_paren _, _ -> true | P_tok, _ -> true let prio_lo_check pri_out pri_in pos expr = if prio_less(pri_in, pri_out) then (match pri_in with | P_paren (P_paren_wanted _) -> () | P_paren pri_in' -> if pri_in' <> pri_out && prio_less(pri_in', pri_out) && not_complex (un_parenthesize expr) then warn [Warn_suggest_simpler] pos "unneeded parentheses" | _ -> ()) else (match expr with | Call_op ("print", [Deref (I_star, Ident (None, "STDOUT", _)); (Deref(I_scalar, _) as ident)], _) -> warn [Warn_traps] pos (sprintf "use parentheses: replace \"print %s ...\" with \"print(%s ...)\"" (string_of_fromparser ident) (string_of_fromparser ident)) | _ -> warn [Warn_traps] pos "missing parentheses (needed for clarity)") let prio_lo pri_out in_ = prio_lo_check pri_out in_.any.priority in_.pos in_.any.expr ; in_.any.expr let prio_lo_after pri_out in_ = if in_.any.priority = P_call_no_paren then in_.any.expr else prio_lo pri_out in_ let prio_lo_concat esp = prio_lo P_mul { esp with any = { esp.any with priority = P_paren_wanted esp.any.priority } } let hash_ref esp = Ref(I_hash, prio_lo P_loose esp) let sp_0 esp = match esp.spaces with | Space_none -> () | Space_0 -> () | Space_1 | Space_n -> warn_space (get_pos_start esp) | Space_cr -> warn_cr (get_pos_start esp) let sp_0_or_cr esp = match esp.spaces with | Space_none -> () | Space_0 -> () | Space_1 | Space_n -> warn_space (get_pos_start esp) | Space_cr -> () let sp_1 esp = match esp.spaces with | Space_none -> () | Space_0 -> warn_no_space (get_pos_start esp) | Space_1 -> () | Space_n -> warn_too_many_space (get_pos_start esp) | Space_cr -> warn_cr (get_pos_start esp) let sp_n esp = match esp.spaces with | Space_none -> () | Space_0 -> warn_no_space (get_pos_start esp) | Space_1 -> () | Space_n -> () | Space_cr -> warn_cr (get_pos_start esp) let sp_p esp = match esp.spaces with | Space_none -> () | Space_0 -> warn_no_space (get_pos_start esp) | Space_1 -> () | Space_n -> () | Space_cr -> () let sp_cr esp = match esp.spaces with | Space_none -> () | Space_0 | Space_1 | Space_n -> warn_verb [Warn_white_space] (get_pos_start esp) "you should have a carriage-return (\\n) here" | Space_cr -> () let sp_same esp1 esp2 = if esp1.spaces <> Space_0 then sp_p esp2 else if esp2.spaces <> Space_0 then sp_p esp1 let function_to_context word_alone = function | "map" | "grep" | "grep_index" | "map_index" | "uniq" | "uniq_" -> M_array | "partition" -> M_tuple [ M_ref M_array ; M_ref M_array ] | "find" -> M_unknown_scalar | "any" | "every" -> M_bool | "find_index" -> M_int | "each_index" -> M_none | "N" | "N_" -> M_string | "chop" | "chomp" | "push" | "unshift" -> M_none | "hex" | "length" | "time" | "fork" | "getppid" -> M_int | "eof" | "wantarray" -> M_int | "stat" | "lstat" -> M_list | "arch" | "quotemeta" | "join" | "lc" | "lcfirst" | "uc" | "ucfirst" -> M_string | "split" -> M_array | "shift" | "pop" -> M_unknown_scalar | "die" | "return" | "redo" | "next" | "last" -> M_unknown | "caller" -> M_mixed [M_string ; M_list] | "ref" -> M_ref M_unknown_scalar | "undef" -> if word_alone then M_undef else M_none | _ -> M_unknown let word_alone esp = let word = esp.any in let mcontext, e = match word with | Ident(None, f, pos) -> let e = match f with | "length" | "stat" | "lstat" | "chop" | "chomp" | "quotemeta" | "lc" | "lcfirst" | "uc" | "ucfirst" -> Call(Deref(I_func, word), [var_dollar_ pos]) | "split" -> Call(Deref(I_func, word), [ Raw_string(" ", pos) ; var_dollar_ pos ]) | "die" -> Call(Deref(I_func, word), [ Deref(I_scalar, Ident(None, "@", raw_pos2pos bpos)) ]) | "return" | "eof" | "caller" | "redo" | "next" | "last" -> Deref(I_func, word) | "hex" | "ref" -> warn_rule [Warn_complex_expressions] (sprintf "please use \"%s $_\" instead of \"%s\"" f f) ; Call(Deref(I_func, word), [ Raw_string(" ", pos) ; var_dollar_ pos ]) | "time" | "wantarray" | "fork" | "getppid" | "arch" -> warn_rule [Warn_complex_expressions] (sprintf "please use %s() instead of %s" f f) ; Deref(I_func, word) | _ -> word in function_to_context true f, e | _ -> M_unknown, word in new_pesp mcontext P_tok e esp esp let check_parenthesized_first_argexpr word esp = let want_space = word.[0] = '-' in if word = "return" then () else match esp.any.expr with | [ Call_op(_, (e' :: l), _) ] | e' :: l -> if is_parenthesized e' then if l = [] then (if want_space then sp_n else sp_0) esp else (* eg: join (" ", @l) . "\n" *) die_with_rawpos (get_pos_start esp, get_pos_start esp) "please remove the space before the function call" else sp_p esp | _ -> if word = "time" then die_rule "please use time() instead of time"; sp_p esp let check_parenthesized_first_argexpr_with_Ident ident esp = if esp.any.priority = P_tok then (); (match ident with | Ident(Some _, _, _) -> (match esp.any.expr with | [e] when is_parenthesized e -> () | _ -> warn_rule [Warn_suggest_simpler] "use parentheses around argument (otherwise it might cause syntax errors if the package is \"require\"d and not \"use\"d") | Ident(None, word, _) when List.mem word ["ref" ; "readlink"] -> if esp.any.priority <> P_tok then warn_rule [Warn_complex_expressions] "use parentheses around argument" | _ -> ()); check_parenthesized_first_argexpr (string_of_fromparser ident) esp let check_hash_subscript esp = let can_be_raw_string = function | "" | "x" | "y" -> false (* special case for {'y'} otherwise the emacs mode goes wild, special case for {'x'} to have the same as {'y'} (since they usually go together) *) | s -> char_is_alpha s.[0] && (String.length s = 1 || string_forall_with char_is_alphanumerical_ 1 s) in match esp.any.expr with | List [String ([(s, List [])], _)] when can_be_raw_string s -> warn [Warn_suggest_simpler] esp.pos (sprintf "{\"%s\"} can be written {%s}" s s) | List [Raw_string(s, _)] when can_be_raw_string s -> warn [Warn_suggest_simpler] esp.pos (sprintf "{'%s'} can be written {%s}" s s) | _ -> () let check_arrow_needed esp1 esp2 = match esp1.any.expr with | Deref_with(I_array, I_scalar, List [List [Call _]], _) -> () (* "->" needed for (f())[0]->{XX} *) | Deref_with _ -> warn [Warn_suggest_simpler] esp2.pos "the arrow \"->\" is unneeded" | _ -> () let check_scalar_subscripted esp = match esp.any with | Deref(I_scalar, Deref _) -> warn_rule [Warn_complex_expressions] "for complex dereferencing, use \"->\"" | _ -> () let negatable_ops = collect (fun (a, b) -> [ a, b ; b, a ]) [ "==", "!=" ; "eq", "ne" ; ] let check_negatable_expr esp = match un_parenthesize_full esp.any.expr with | Call_op("m//", var :: _, _) when not (is_var_dollar_ var) -> warn_rule [Warn_suggest_simpler] "!($var =~ /.../) is better written $var !~ /.../" | Call_op("!m//", var :: _, _) when not (is_var_dollar_ var) -> warn_rule [Warn_suggest_simpler] "!($var !~ /.../) is better written $var =~ /.../" | Call_op(op, _, _) -> (try let neg_op = List.assoc op negatable_ops in warn_rule [Warn_suggest_simpler] (Printf.sprintf "!($foo %s $bar) is better written $foo %s $bar" op neg_op) with Not_found -> ()) | _ -> () let check_ternary_paras(cond, a, b) = let rec dont_need_short_circuit_rec = function | Num _ | Raw_string _ | String ([(_, List [])], _) -> true | Call(Deref(I_func, Ident(None, "N", _)), [ List(String _ :: l) ]) | Call_op(".", l, _) | Ref(I_hash, List l) | List l -> List.for_all dont_need_short_circuit_rec l | _ -> false in let rec dont_need_short_circuit = function | Ref(_, Deref(_, Ident _)) | Deref(_, Ident _) -> true | Ref(I_hash, List l) | List l -> List.for_all dont_need_short_circuit l | e -> dont_need_short_circuit_rec e in let check_ternary_para = function | List [] -> warn_rule [Warn_suggest_simpler; Warn_MDK_Common] "you may use if_() here\n beware that the short-circuit semantic of ?: is not kept\n if you want to keep the short-circuit behaviour, replace () with @{[]} and there will be no warning anymore" | _ -> () in if dont_need_short_circuit a || is_same_fromparser cond a then check_ternary_para b; if dont_need_short_circuit b || is_same_fromparser cond b then check_ternary_para a; if is_same_fromparser cond a && is_a_scalar a && is_a_scalar b then warn_rule [Warn_suggest_simpler] "you can replace \"$foo ? $foo : $bar\" with \"$foo || $bar\""; [ cond; a; b ] let check_unneeded_var_dollar_ esp = if is_var_dollar_ esp.any.expr then warn [Warn_suggest_simpler] esp.pos "\"$_ =~ /regexp/\" can be written \"/regexp/\"" else if is_var_number_match esp.any.expr then warn [Warn_complex_expressions] esp.pos "do not use the result of a match (eg: $1) to match another pattern" let check_unneeded_var_dollar_not esp = if is_var_dollar_ esp.any.expr then warn [Warn_suggest_simpler] esp.pos "\"$_ !~ /regexp/\" can be written \"!/regexp/\"" else if is_var_number_match esp.any.expr then warn [Warn_complex_expressions] esp.pos "do not use the result of a match (eg: $1) to match another pattern" let check_unneeded_var_dollar_s esp = let expr = esp.any.expr in if is_var_dollar_ expr then warn [Warn_suggest_simpler] esp.pos "\"$_ =~ s/regexp/.../\" can be written \"s/regexp/.../\"" else if is_var_number_match expr then warn [Warn_traps] esp.pos "do not modify the result of a match (eg: $1)" else let expr = match expr with | List [List [Call_op("=", [ expr; _], _)]] -> expr (* check $xx in ($xx = ...) =~ ... *) | _ -> expr in if is_a_string expr || not (is_a_scalar expr) then warn [Warn_complex_expressions] esp.pos "you can only use s/// on a variable" let check_my esp = if esp.any <> "my" then die_rule "syntax error" let check_foreach esp = if esp.any = "for" then warn [Warn_normalized_expressions] esp.pos "write \"foreach\" instead of \"for\"" let check_for esp = if esp.any = "foreach" then warn [Warn_normalized_expressions] esp.pos "write \"for\" instead of \"foreach\"" let check_for_foreach esp arg = match arg.any.expr with | List [ Deref(I_scalar, _) ] -> if esp.any = "foreach" then warn [Warn_normalized_expressions] esp.pos "you are using the special trick to locally set $_ with a value, for this please use \"for\" instead of \"foreach\"" | List [ Deref_with(context, I_scalar, _, _) ] when context <> I_func -> if esp.any = "foreach" then warn [Warn_normalized_expressions] esp.pos "you are using the special trick to locally set $_ with a value, for this please use \"for\" instead of \"foreach\"" | List [ Deref(I_hash, _) ] -> warn [Warn_traps] esp.pos "foreach with a hash is usually an error" | _ -> if esp.any = "for" then warn [Warn_normalized_expressions] esp.pos "write \"foreach\" instead of \"for\"" let check_block_sub esp_lines esp_BRACKET_END = match fst esp_lines.any with | [] -> sp_0_or_cr esp_BRACKET_END | l -> (if List.hd l = Semi_colon then sp_0 else sp_p) esp_lines ; sp_p esp_BRACKET_END ; if esp_BRACKET_END.spaces = Space_cr then (if not (snd esp_lines.any) then warn_verb [Warn_white_space] (get_pos_end esp_lines) "missing \";\"") else (if last l = Semi_colon then warn_verb [Warn_white_space] (get_pos_end esp_lines) "spurious \";\" before closing block") let check_block_ref esp_lines esp_BRACKET_END = let l = esp_lines.any in if l <> [] && List.hd l = Semi_colon then (sp_0 esp_lines ; sp_p esp_BRACKET_END) else sp_same esp_lines esp_BRACKET_END ; if esp_BRACKET_END.spaces <> Space_cr then (if l <> [] && last l = Semi_colon then warn_verb [Warn_white_space] (get_pos_end esp_lines) "spurious \";\" before closing block") let check_unless_else elsif else_ = if elsif.any <> [] then warn [Warn_complex_expressions] elsif.pos "don't use \"elsif\" with \"unless\" (replace \"unless\" with \"if\")"; if else_.any <> [] then warn [Warn_complex_expressions] else_.pos "don't use \"else\" with \"unless\" (replace \"unless\" with \"if\")" let check_my_our_paren { any = ((comma_closed, _), l) } after_esp = (if l = [] then sp_0 else sp_1) after_esp ; if not comma_closed then die_rule "syntax error" let check_simple_pattern = function | [ String([ st, List [] ], _); Raw_string("", _) ] -> if String.length st > 2 && st.[0] = '^' && st.[String.length st - 1] = '$' then let st = skip_n_char_ 1 1 st in if string_forall_with char_is_alphanumerical_ 0 st then warn_rule [Warn_suggest_simpler] (sprintf "\"... =~ /^%s$/\" is better written \"... eq '%s'\"" st st) | _ -> () let rec only_one esp = match esp.any with | [List l'] -> only_one { esp with any = l' } | [e] -> e | [] -> die_with_rawpos esp.pos "you must give one argument" | _ -> die_with_rawpos esp.pos "you must give only one argument" let only_one_array_ref esp = let e = only_one esp in (match e with | Call_op("last_array_index", [Deref(I_array, e)], _) -> warn [Warn_suggest_simpler] esp.pos (sprintf "you can replace $#%s with -1" (string_of_fromparser e)) | _ -> ()); e let only_one_in_List esp = match esp.any.expr with | List l -> only_one { esp with any = l } | e -> e let rec is_only_one_in_List = function | [List l] -> is_only_one_in_List l | [_] -> true | _ -> false let maybe_to_Raw_string = function | Ident(None, s, pos) -> Raw_string(s, pos) | Ident(Some fq, s, pos) -> Raw_string(fq ^ "::" ^ s, pos) | e -> e let to_List = function | [e] -> e | l -> List l let deref_arraylen e = Call_op("last_array_index", [Deref(I_array, e)], raw_pos2pos bpos) let deref_raw context e = let e = match e with | Raw_string(s, pos) -> let fq, ident = split_name_or_fq_name s in Ident(fq, ident, pos) | Deref(I_scalar, (Ident _ as ident)) -> warn_rule [Warn_suggest_simpler] (sprintf "%s{$%s} can be written %s$%s" (context2s context) (string_of_fromparser ident) (context2s context) (string_of_fromparser ident)); e | _ -> e in Deref(context, e) let to_Ident { any = (fq, name); pos = pos } = Ident(fq, name, raw_pos2pos pos) let to_Raw_string { any = s; pos = pos } = Raw_string(s, raw_pos2pos pos) let to_Method_call (object_, method_, para) = match method_ with | Ident(Some "SUPER", name, pos) -> Method_call(maybe_to_Raw_string object_, Raw_string(name, pos), para) | Ident(Some _, _, _) -> Call(Deref(I_func, method_), maybe_to_Raw_string object_ :: para) | _ -> Method_call(maybe_to_Raw_string object_, maybe_to_Raw_string method_, para) let to_Deref_with(from_context, to_context, ref_, para) = if is_not_a_scalar ref_ then warn_rule [] "bad deref"; Deref_with(from_context, to_context, ref_, para) let lines_to_Block esp_lines esp_BRACKET_END = check_block_sub esp_lines esp_BRACKET_END; Block (fst esp_lines.any) let to_Local esp = let l = match esp.any.expr with | List[List l] -> l | e -> [e] in let local_vars, local_exprs = fpartition (function | Deref(I_star as context, Ident(None, ident, _)) | Deref(I_scalar as context, Ident(None, ("_" as ident), _)) -> Some(context, ident) | Deref(I_scalar, Ident _) | Deref(I_array, Ident _) | Deref(I_star, Ident _) | Deref_with(I_hash, I_scalar, Ident _, _) | Deref_with(I_hash, I_scalar, Deref(I_scalar, _), _) | Deref_with(I_hash, I_scalar, Deref_with(I_hash, I_scalar, Ident _, _), _) | Deref_with(I_hash, I_scalar, Deref_with(I_hash, I_scalar, Deref(I_scalar, Ident _), _), _) -> None | _ -> die_with_rawpos esp.pos "bad argument to \"local\"" ) l in if local_vars = [] then Call_op("local", local_exprs, raw_pos2pos esp.pos) else if local_exprs = [] then My_our("local", local_vars, raw_pos2pos esp.pos) else die_with_rawpos esp.pos "bad argument to \"local\"" let sub_declaration (name, proto) body sub_kind = Sub_declaration(name, proto, Block body, sub_kind) let anonymous_sub proto lines bracket_end = Anonymous_sub (proto, lines_to_Block lines bracket_end, raw_pos2pos lines.pos) let call_with_same_para_special f = Call(f, [Deref(I_star, (Ident(None, "_", raw_pos2pos bpos)))]) let remove_call_with_same_para_special = function | Call(f, [Deref(I_star, (Ident(None, "_", _)))]) -> f | e -> e let check_My_under_condition msg = function | List [ My_our("my", _, _) ] -> warn_rule [Warn_traps] "this is stupid" | List [ Call_op("=", [ My_our("my", _, _); _ ], _) ] -> warn_rule [Warn_traps] msg | _ -> () let cook_call_op op para pos = (match op with | "le" | "ge" | "eq" | "ne" | "gt" | "lt" | "cmp" -> if List.exists (function Num _ -> true | _ -> false) para then warn_rule [Warn_traps] (sprintf "you should use a number operator, not the string operator \"%s\" (or replace the number with a string)" op) | "." -> if List.exists (function Call(Deref(I_func, Ident(None, "N_", _)), _) -> true | _ -> false) para then warn_rule [Warn_MDK_Common; Warn_traps] "N_(\"xxx\") . \"yyy\" is dumb since the string \"xxx\" will never get translated" | _ -> ()); (match op, para with | "if", List [Call_op ("=", [ _; e ], _)] :: _ when is_always_true e || is_always_false e -> warn_rule [Warn_traps] "are you sure you did not mean \"==\" instead of \"=\"?" | "foreach", [ _; Block [ expr ; Semi_colon ] ] | "foreach", [ _; Block [ expr ] ] -> (match expr with | Call_op("if infix", [ List [ Call(Deref(I_func, Ident(None, "push", _)), [ Deref(I_array, Ident _) as l ; Deref(I_scalar, Ident(None, "_", _)) ]) ] ; _ ], _) -> let l = string_of_fromparser l in warn_rule [Warn_suggest_functional] (sprintf "use \"push %s, grep { ... } ...\" instead of \"foreach (...) { push %s, $_ if ... }\"\n or sometimes \"%s = grep { ... } ...\"" l l l) | Call_op("if infix", [ List [ Call(Deref(I_func, Ident(None, "push", _)), [ Deref(I_array, Ident _) as l; _ ]) ] ; _ ], _) -> let l = string_of_fromparser l in warn_rule [Warn_suggest_functional] (sprintf "use \"push %s, map { ... ? ... : () } ...\" instead of \"foreach (...) { push %s, ... if ... }\"\n or sometimes \"%s = map { ... ? ... : () } ...\"\n or sometimes \"%s = map { if_(..., ...) } ...\"" l l l l) | Call_op ("if", [ _; Block [ List [ Call_op("=", [Deref(I_scalar, _) as ret; Deref(I_scalar, Ident(None, "_", _)) ], _) ]; Semi_colon; List [ Deref(I_func, Ident(None, "last", _)) ]; Semi_colon ] ], _) -> warn_rule [Warn_suggest_functional; Warn_MDK_Common] (sprintf "use \"%s = find { ... } ...\"" (string_of_fromparser ret)) | List [ Call(Deref(I_func, Ident(None, "push", _)), [ Deref(I_array, Ident _) as l; _ ]) ] -> let l = string_of_fromparser l in warn_rule [Warn_suggest_functional] (sprintf "use \"push %s, map { ... } ...\" instead of \"foreach (...) { push %s, ... }\"\n or sometimes \"%s = map { ... } ...\"" l l l) | _ -> ()) | "=", [My_our _; Ident(None, "undef", _)] -> warn [Warn_suggest_simpler] pos "no need to initialize variable, it's done by default" | "=", [My_our _; List[]] -> if Info.is_on_same_line_current pos then warn [Warn_suggest_simpler] pos "no need to initialize variables, it's done by default" | "=", [ Deref_with(I_array, I_scalar, id, Deref(I_array, id_)); _ ] when is_same_fromparser id id_ -> warn_rule [Warn_suggest_simpler] "\"$a[@a] = ...\" is better written \"push @a, ...\"" | "=", [ Deref(I_star, String ([(sf1, List [])], _)); _ ] -> warn_rule [Warn_help_perl_checker] (sprintf "write *{'%s'} instead of *{\"%s\"}" sf1 sf1) | "||=", List [ List _ ] :: _ | "&&=", List [ List _ ] :: _ -> warn_rule [Warn_complex_expressions] "remove the parentheses" | "||=", e :: _ | "&&=", e :: _ -> if is_not_a_scalar e then warn_rule [Warn_traps] (sprintf "\"%s\" is only useful with a scalar" op) | "==", [Call_op("last_array_index", _, _); Num(n, _)] -> warn_rule [Warn_suggest_simpler] (sprintf "$#x == %s is better written @x == %d" n (1 + int_of_string n)) | "==", [Call_op("last_array_index", _, _); Call_op("- unary", [Num (n, _)], _)] -> warn_rule [Warn_suggest_simpler] (sprintf "$#x == -%s is better written @x == %d" n (1 - int_of_string n)) | "||", e :: _ when is_always_true e -> warn_rule [Warn_strange] " || ... is the same as " | "&&", e :: _ when is_always_false e -> warn_rule [Warn_strange] " && ... is the same as " | "||", e :: _ when is_always_false e -> warn_rule [Warn_strange] " || ... is the same as ..." | "&&", e :: _ when is_always_true e -> warn_rule [Warn_strange] " && ... is the same as ..." | "or", e :: _ when is_always_true (un_parenthesize_full e) -> warn_rule [Warn_strange] " or ... is the same as " | "and", e :: _ when is_always_false (un_parenthesize_full e) -> warn_rule [Warn_strange] " and ... is the same as " | "or", e :: _ when is_always_false (un_parenthesize_full e) -> warn_rule [Warn_strange] " or ... is the same as ..." | "and", e :: _ when is_always_true (un_parenthesize_full e) -> warn_rule [Warn_strange] " and ... is the same as ..." | "or", [ List [ Deref(I_scalar, id) ]; List [ Call_op("=", [ Deref(I_scalar, id_); _], _) ] ] when is_same_fromparser id id_ -> warn_rule [Warn_suggest_simpler] "\"$foo or $foo = ...\" can be written \"$foo ||= ...\"" | "and", [ _cond ; expr ] -> check_My_under_condition "replace \" and my $foo = ...\" with \"my $foo = && ...\"" expr | "or", [ _cond ; expr ] -> check_My_under_condition "replace \" or my $foo = ...\" with \"my $foo = ! && ...\"" expr | _ -> ()); match op, para with | "=", [ Deref(I_star, (Ident _ as f1)); Deref(I_star, (Ident _ as f2)) ] -> let s1, s2 = string_of_fromparser f1, string_of_fromparser f2 in warn [Warn_complex_expressions] pos (sprintf "\"*%s = *%s\" is better written \"*%s = \\&%s\"" s1 s2 s1 s2) ; sub_declaration (f1, None) [ call_with_same_para_special(Deref(I_func, f2)) ] Glob_assign | "=", [ Deref(I_star, Raw_string(sf1, pos_f1)); Deref(I_star, (Ident _ as f2)) ] -> let s2 = string_of_fromparser f2 in warn [Warn_help_perl_checker] pos (sprintf "\"*{'%s'} = *%s\" is better written \"*{'%s'} = \\&%s\"" sf1 s2 sf1 s2) ; sub_declaration (Ident(None, sf1, pos_f1), None) [ call_with_same_para_special(Deref(I_func, f2)) ] Glob_assign | "=", [ Deref(I_star, (Ident _ as f1)); Ref(I_scalar, Deref(I_func, (Ident _ as f2))) ] -> sub_declaration (f1, None) [ call_with_same_para_special(Deref(I_func, f2)) ] Glob_assign | "=", [ Deref(I_star, Raw_string(sf1, pos_f1)); Ref(I_scalar, Deref(I_func, (Ident _ as f2))) ] -> sub_declaration (Ident(None, sf1, pos_f1), None) [ call_with_same_para_special(Deref(I_func, f2)) ] Glob_assign | "=", [ Deref(I_star, (Ident _ as f1)); (Anonymous_sub(proto, sub, _)) ] -> sub_declaration (f1, proto) [ sub ] Glob_assign | _ -> Call_op(op, para, raw_pos2pos pos) let to_Call_op mcontext op para esp_start esp_end = let pos = raw_pos_range esp_start esp_end in new_any mcontext (cook_call_op op para pos) esp_start.spaces pos let to_Call_op_ mcontext prio op para esp_start esp_end = let pos = raw_pos_range esp_start esp_end in new_any mcontext { priority = prio ; expr = cook_call_op op para pos } esp_start.spaces pos let to_Call_assign_op_ mcontext prio op left right esp_left esp_end = if not (is_lvalue left) then warn [Warn_strange] esp_left.pos "invalid lvalue"; to_Call_op_ mcontext prio op [ left ; right ] esp_left esp_end let followed_by_comma expr true_comma = if true_comma then expr else match split_last expr with | l, Ident(None, s, pos) -> l @ [Raw_string(s, pos)] | _ -> expr let pot_strings = Hashtbl.create 16 let po_comments = ref [] let po_comment esp = lpush po_comments esp.any let check_format_a_la_printf s pos = let rec check_format_a_la_printf_ contexts i = try let i' = String.index_from s i '%' in try let contexts = match s.[i' + 1] with | '%' -> contexts | 'd' -> M_int :: contexts | 's' | 'c' -> M_string :: contexts | c -> warn [Warn_strange] (pos + i', pos + i') (sprintf "invalid command %%%c" c); contexts in check_format_a_la_printf_ contexts (i' + 2) with Invalid_argument _ -> warn [Warn_strange] (pos + i', pos + i') "invalid command %" ; contexts with Not_found -> contexts in check_format_a_la_printf_ [] 0 let generate_pot file = let fd = open_out file in output_string fd ("# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Free Software Foundation, Inc. # FIRST AUTHOR , YEAR. # #, fuzzy msgid \"\" msgstr \"\" \"Project-Id-Version: PACKAGE VERSION\\n\" \"POT-Creation-Date: " ^ input_line (Unix.open_process_in "date '+%Y-%m-%d %H:%M%z'") ^ "\\n\" \"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\" \"Last-Translator: FULL NAME \\n\" \"Language-Team: LANGUAGE \\n\" \"MIME-Version: 1.0\\n\" \"Content-Type: text/plain; charset=CHARSET\\n\" \"Content-Transfer-Encoding: 8-bit\\n\" ") ; let rec print_formatted_char = function | '"' -> output_char fd '\\'; output_char fd '"' | '\t' -> output_char fd '\\'; output_char fd 't' | '\\' -> output_char fd '\\'; output_char fd '\\' | '\n' -> output_string fd "\\n\"\n\"" | c -> output_char fd c in let sorted_pot_strings = List.sort (fun (_, pos_a) (_, pos_b) -> compare pos_a pos_b) (Hashtbl.fold (fun k (v, _) l -> (k,v) :: l) pot_strings [] ) in List.iter (fun (s, _) -> match Hashtbl.find_all pot_strings s with | [] -> () | l -> List.iter (fun _ -> Hashtbl.remove pot_strings s) l ; List.iter (fun po_comment -> output_string fd ("#. " ^ po_comment ^ "\n")) (collect snd l); let pos_l = List.sort compare (List.map fst l) in fprintf fd "#: %s\n" (String.concat " " (List.map Info.pos2s_for_po pos_l)) ; output_string fd "#, c-format\n" ; output_string fd (if String.contains s '\n' then "msgid \"\"\n\"" else "msgid \"") ; String.iter print_formatted_char s ; output_string fd "\"\n" ; output_string fd "msgstr \"\"\n\n" ) sorted_pot_strings ; close_out fd let check_system_call = function | "mkdir" :: l -> let has_p = List.exists (str_begins_with "-p") l in let has_m = List.exists (str_begins_with "-m") l in if has_p && has_m then () else if has_p then warn_rule [Warn_suggest_simpler; Warn_MDK_Common] "you can replace system(\"mkdir -p ...\") with mkdir_p(...)" else if has_m then warn_rule [Warn_suggest_simpler; Warn_MDK_Common] "you can replace system(\"mkdir -m ...\") with mkdir(..., )" else warn_rule [Warn_suggest_simpler; Warn_MDK_Common] "you can replace system(\"mkdir ...\") with mkdir(...)" | _ -> () let call_raw force_non_builtin_func (e, para) = let check_anonymous_block f = function | [ Anonymous_sub _ ; Deref (I_hash, _) ] -> warn_rule [Warn_strange] ("a hash is not a valid parameter to function " ^ f) | Anonymous_sub _ :: _ -> () | _ -> warn_rule [Warn_complex_expressions] (sprintf "always use \"%s\" with a block (eg: %s { ... } @list)" f f) in match e with | Deref(I_func, Ident(None, f, _)) -> (match f with | "join" -> (match un_parenthesize_full_l para with | e :: _ when not (is_a_scalar e) -> warn_rule [Warn_traps] "first argument of join() must be a scalar"; | [_] -> warn_rule [Warn_traps] "not enough parameters" | [_; e] when is_a_scalar e -> warn_rule [Warn_traps] "join('...', $foo) is the same as $foo" | _ -> ()) | "length" -> if para = [] then warn_rule [Warn_complex_expressions] (sprintf "please use \"%s $_\" instead of \"%s\"" f f) else if is_not_a_scalar (List.hd para) then warn_rule [Warn_traps] "never use \"length @l\", it returns the length of the string int(@l)" ; | "open" -> (match para with | [ List(Ident(None, name, _) :: _) ] | Ident(None, name, _) :: _ -> if not (List.mem name [ "STDIN" ; "STDOUT" ; "STDERR" ]) then warn_rule [Warn_complex_expressions] (sprintf "use a scalar instead of a bareword (eg: occurrences of %s with $%s)" name name) | _ -> ()) | "N" | "N_" -> (match para with | [ List(String([ s, List [] ], (_, pos_offset, _ as pos)) :: para) ] -> if !Flags.generate_pot then ( Hashtbl.add pot_strings s (pos, !po_comments) ; po_comments := [] ) ; let contexts = check_format_a_la_printf s pos_offset in if f = "N" then if List.length para < List.length contexts then warn_rule [Warn_traps; Warn_MDK_Common] "not enough parameters" else if List.length para > List.length contexts then warn_rule [Warn_traps; Warn_MDK_Common] "too many parameters" ; (*if String.contains s '\t' then warn_rule "tabulation in translated string must be written \\\\t";*) (*if count_matching_char s '\n' > 10 then warn_rule "long string";*) | [ List(String _ :: _) ] -> die_rule "don't use interpolated translated string, use %s or %d instead" | _ -> die_rule (sprintf "%s() must be used with a string" f)) | "if_" -> (match para with | [ List [ _ ] ] -> warn_rule [Warn_traps; Warn_MDK_Common] "not enough parameters"; | _ -> ()) | "map" -> (match para with | Anonymous_sub(None, Block [ List [ Call(Deref(I_func, Ident(None, "if_", _)), [ List [ _ ; Deref(I_scalar, Ident(None, "_", _)) ] ]) ] ], _) :: _ -> warn_rule [Warn_suggest_simpler; Warn_MDK_Common] "you can replace \"map { if_(..., $_) }\" with \"grep { ... }\"" | _ -> check_anonymous_block f para) | "grep" -> (match para with | [ Anonymous_sub(None, Block [ List [ Call_op("not", [ Call(Deref(I_func, Ident(None, "member", _)), [ List(Deref(I_scalar, Ident(None, "_", _)) :: _) ]) ], _) ] ], _); _ ] -> warn_rule [Warn_suggest_simpler; Warn_MDK_Common] "you can replace \"grep { !member($_, ...) } @l\" with \"difference2([ @l ], [ ... ])\"" | [ Anonymous_sub(None, Block [ List [ Call(Deref(I_func, Ident(None, "member", _)), [ List(Deref(I_scalar, Ident(None, "_", _)) :: _) ]) ] ], _); _ ] -> warn_rule [Warn_suggest_simpler; Warn_MDK_Common] "you can replace \"grep { member($_, ...) } @l\" with \"intersection([ @l ], [ ... ])\"" | _ -> check_anonymous_block f para) | "any" -> (match para with [Anonymous_sub (None, Block [ List [ Call_op("eq", [Deref(I_scalar, Ident(None, "_", _)); _ ], _) ] ], _); _ ] -> warn_rule [Warn_suggest_simpler; Warn_MDK_Common] "you can replace \"any { $_ eq ... } @l\" with \"member(..., @l)\"" | _ -> check_anonymous_block f para) | "grep_index" | "map_index" | "partition" | "uniq_" | "find" | "every" | "find_index" | "each_index" -> check_anonymous_block f para | "member" -> (match para with [ List [ _; Call(Deref(I_func, Ident(None, "keys", _)), _) ] ] -> warn_rule [Warn_suggest_simpler; Warn_MDK_Common] "you can replace \"member($xxx, keys %yyy)\" with \"exists $yyy{$xxx}\"" | _ -> ()) | "pop" | "shift" -> (match para with | [] | [ Deref(I_array, _) ] | [ List [ Deref(I_array, _) ] ] -> () | _ -> warn_rule [Warn_traps] (f ^ " is expecting an array and nothing else")) | "push" | "unshift" -> (match para with | Deref(I_array, _) :: l | [ List (Deref(I_array, _) :: l) ] -> if l = [] then warn_rule [Warn_traps] ("you must give some arguments to " ^ f) | _ -> warn_rule [Warn_traps] (f ^ " is expecting an array")) | "system" -> let fake_string_option_from_expr = function | String(l, _) -> Some(String.concat "" (List.map fst l)) | Raw_string(s, _) -> Some s | _ -> None in (match un_parenthesize_full_l para with | [ e ] -> (match fake_string_option_from_expr e with | Some s -> if List.exists (String.contains s) [ '\'' ; char_quote ] && not (List.exists (String.contains s) [ '<' ; '>' ; '&' ; ';']) then warn_rule [Warn_complex_expressions] "instead of quoting parameters you should give a list of arguments"; check_system_call (split_at ' ' s) | None -> ()) | l -> let l' = filter_some_with fake_string_option_from_expr l in check_system_call l') | _ -> () ); let para' = match f with | "no" -> (match para with | [ Ident(_, _, pos) as s ] -> Some [ Raw_string(string_of_fromparser s, pos) ] | [ Call(Deref(I_func, (Ident(_, _, pos) as s)), l) ] -> Some(Raw_string(string_of_fromparser s, pos) :: l) | _ -> die_rule "use \"no PACKAGE \"") | "undef" -> (match para with | [ Deref(I_star, ident) ] -> Some [ Deref(I_func, ident) ] | _ -> None) | "goto" -> (match para with | [ Ident(None, s, pos) ] -> Some [ Raw_string(s, pos) ] | _ -> None) | "last" | "next" | "redo" when not force_non_builtin_func -> (match para with | [ Ident(None, s, pos) ] -> Some [ Raw_string(s, pos) ] | _ -> die_rule (sprintf "%s must be used with a raw string" f)) | "split" -> (match para with | [ List(Call_op("m//", Deref(I_scalar, Ident(None, "_", _)) :: pattern, pos) :: l) ] | Call_op("m//", Deref(I_scalar, Ident(None, "_", _)) :: pattern, pos) :: l -> Some(Call_op("qr//", pattern, pos) :: l) | _ -> None) | _ -> None in Call(e, some_or para' para) | _ -> Call(e, para) let call(e, para) = call_raw false (e, para) let check_return esp_func esp_para = match esp_func.any with | Ident(None, "return", _) -> prio_lo_check P_call_no_paren esp_para.any.priority esp_para.pos (List esp_para.any.expr) | _ -> () let call_and_context(e, para) force_non_builtin_func priority esp_start esp_end = let context = match e with | Deref(I_func, Ident(None, f, _)) -> function_to_context false f | _ -> M_unknown in new_pesp context priority (call_raw force_non_builtin_func (e, para)) esp_start esp_end let call_no_paren esp_func esp_para = check_return esp_func esp_para; call_and_context(Deref(I_func, esp_func.any), esp_para.any.expr) false P_call_no_paren esp_func esp_para let call_with_paren esp_func esp_para = check_return esp_func esp_para; call_and_context (Deref(I_func, esp_func.any), esp_para.any.expr) false P_tok esp_func esp_para let call_func esp_func esp_para = call_and_context(esp_func.any, esp_para.any.expr) true P_tok esp_func esp_para let call_one_scalar_para { any = e ; pos = pos } para esp_start esp_end = let para = match para with | [] -> if e = "shift" || e = "pop" then [] (* can't decide here *) else (if not (List.mem e [ "length" ]) then warn_rule [Warn_complex_expressions] (sprintf "please use \"%s $_\" instead of \"%s\"" e e) ; [var_dollar_ (raw_pos2pos pos)]) | _ -> para in new_pesp M_unknown P_mul (call(Deref(I_func, Ident(None, e, raw_pos2pos pos)), para)) esp_start esp_end let (current_lexbuf : Lexing.lexbuf option ref) = ref None let rec list2tokens l = let rl = ref l in fun lexbuf -> match !rl with | [] -> internal_error "list2tokens" | ((start, end_), e) :: l -> (* HACK: fake a normal lexbuf *) lexbuf.Lexing.lex_start_p <- { Lexing.dummy_pos with Lexing.pos_cnum = start } ; lexbuf.Lexing.lex_curr_p <- { Lexing.dummy_pos with Lexing.pos_cnum = end_ } ; rl := l ; e let parse_tokens parse tokens lexbuf_opt = if lexbuf_opt <> None then current_lexbuf := lexbuf_opt ; if tokens = [] then [] else parse (list2tokens tokens) (some !current_lexbuf) let parse_interpolated parse l = let l' = List.map (fun (s, tokens) -> s, to_List(parse_tokens parse tokens None)) l in match split_last l' with | pl, ("", List []) -> pl | _ -> l' let to_String parse strict { any = l ; pos = pos } = let l' = parse_interpolated parse l in (match l' with | [ "", List [Deref(I_scalar, Ident(None, ident, _))]] -> if ident <> "!" && strict then warn [Warn_suggest_simpler] pos (sprintf "%s is better written without the double quotes" (variable2s(I_scalar, ident))) | [ "", List [Deref(I_hash, _)]] -> warn [Warn_traps] pos "don't use a hash in string context" | [ "", List [Deref(I_array, _)]] | [ "", List [Deref_with(I_array, I_array, _, _)]] -> (* for slices like: "@m3[1..$#m3]" *) () | [("", _)] -> if strict then warn [Warn_suggest_simpler] pos "double quotes are unneeded" | _ -> ()); String(l', raw_pos2pos pos) let from_PATTERN parse { any = (s, opts) ; pos = pos } = let re = parse_interpolated parse s in (match List.rev re with | (s, List []) :: _ -> if str_ends_with s ".*" then warn_rule [Warn_suggest_simpler] (sprintf "you can remove \"%s\" at the end of your regexp" ".*") else if str_ends_with s ".*$" then warn_rule [Warn_suggest_simpler] (sprintf "you can remove \"%s\" at the end of your regexp" ".*$") | _ -> ()); let pattern = [ String(re, raw_pos2pos pos) ; Raw_string(opts, raw_pos2pos pos) ] in check_simple_pattern pattern; pattern let from_PATTERN_SUBST parse { any = (s1, s2, opts) ; pos = pos } = [ String(parse_interpolated parse s1, raw_pos2pos pos) ; String(parse_interpolated parse s2, raw_pos2pos pos) ; Raw_string(opts, raw_pos2pos pos) ] let rec mcontext2s = function | M_none -> "()" | M_bool -> "bool" | M_int -> "int" | M_float -> "float" | M_string -> "string" | M_ref c -> "ref(" ^ mcontext2s c ^ ")" | M_revision -> "revision" | M_undef -> "undef" | M_sub -> "sub" | M_unknown_scalar -> "scalar" | M_tuple l -> "tuple(" ^ String.concat ", " (List.map mcontext2s l) ^ ")" | M_list -> "list" | M_array -> "array" | M_hash -> "hash" | M_special -> "special" | M_unknown -> "unknown" | M_mixed l -> String.concat " | " (List.map mcontext2s l) let rec mcontext_lower c1 c2 = match c1, c2 with | M_special, _ | _, M_special -> internal_error "M_special in mcontext_compare" | M_unknown, _ | _, M_unknown -> true | M_mixed l, c -> List.exists (fun a -> mcontext_lower a c) l | c, M_mixed l -> List.exists (mcontext_lower c) l | M_none, M_none | M_sub, M_sub | M_hash, M_hash | M_hash, M_bool -> true | M_none, _ | M_sub, _ | M_hash, _ -> false | _, M_list -> true | M_list, M_bool | M_list, M_tuple _ (* M_unknown_scalar is M_mixed [ M_int ; M_float ; M_string ; M_bool ; M_ref _ ; M_revision ; M_undef ] *) | M_unknown_scalar, M_int | M_unknown_scalar, M_float | M_unknown_scalar, M_string | M_unknown_scalar, M_bool | M_unknown_scalar, M_ref _ | M_unknown_scalar, M_revision | M_unknown_scalar, M_undef | M_unknown_scalar, M_unknown_scalar | M_array, M_array | M_array, M_int | M_array, M_float | M_array, M_bool | M_array, M_unknown_scalar | M_array, M_tuple _ | M_int, M_int | M_int, M_float | M_int, M_string | M_int, M_bool | M_int, M_unknown_scalar | M_float, M_float | M_float, M_string | M_float, M_bool | M_float, M_unknown_scalar | M_string, M_string | M_string, M_bool | M_string, M_unknown_scalar | M_bool, M_bool | M_bool, M_unknown_scalar | M_ref _, M_unknown_scalar | M_revision, M_revision | M_revision, M_unknown_scalar | M_undef, M_undef | M_undef, M_unknown_scalar -> true | M_tuple t1, M_tuple t2 -> List.length t1 = List.length t2 && for_all2_true mcontext_lower t1 t2 | M_tuple [c], M_int | M_tuple [c], M_float | M_tuple [c], M_string | M_tuple [c], M_bool | M_tuple [c], M_ref _ | M_tuple [c], M_revision | M_tuple [c], M_undef | M_tuple [c], M_unknown_scalar -> mcontext_lower c c2 (* | M_ref a, M_ref b -> mcontext_lower a b *) | _ -> false let mcontext_is_scalar = function | M_unknown -> false | c -> mcontext_lower c M_unknown_scalar let mcontext_to_scalar = function | M_array -> M_int | c -> if mcontext_is_scalar c then c else M_unknown_scalar let mcontext_merge_raw c1 c2 = match c1, c2 with | M_unknown, _ | _, M_unknown -> Some M_unknown | M_unknown_scalar, c when mcontext_is_scalar c -> Some M_unknown_scalar | c, M_unknown_scalar when mcontext_is_scalar c -> Some M_unknown_scalar | M_mixed _, _ | _, M_mixed _ -> internal_error "mcontext_merge_raw" | _ -> if mcontext_lower c1 c2 then Some c2 else if mcontext_lower c2 c1 then Some c1 else if c1 = c2 then Some c1 else None let rec mcontext_lmerge_add l = function | M_mixed l2 -> List.fold_left mcontext_lmerge_add [] (l2 @ l) | c -> let rec add_to = function | [] -> [c] | M_mixed subl :: l -> add_to (subl @ l) | c2 :: l -> match mcontext_merge_raw c c2 with | Some c' -> c' :: l | None -> c2 :: add_to l in add_to l let mcontext_lmerge l = match List.fold_left mcontext_lmerge_add [] l with | [] -> internal_error "mcontext_lmerge" | [c] -> c | l -> M_mixed l let mcontext_merge c1 c2 = mcontext_lmerge [ c1 ; c2 ] let mcontext_lmaybe esp = if esp.any = [] then [] else [esp.mcontext] let mcontext_check_raw wanted_mcontext mcontext = if not (mcontext_lower mcontext wanted_mcontext) then warn_rule [Warn_context] (sprintf "context %s is not compatible with context %s" (mcontext2s mcontext) (mcontext2s wanted_mcontext)) let mcontext_check wanted_mcontext esp = (match wanted_mcontext with | M_list | M_array | M_float | M_mixed [M_array; M_none] | M_tuple _ -> () | _ -> match un_parenthesize_full esp.any.expr with | Call(Deref(I_func, Ident(None, "grep", _)), _) -> warn_rule [Warn_suggest_simpler; Warn_help_perl_checker] (if wanted_mcontext = M_bool then "in boolean context, use \"any\" instead of \"grep\"" else "you may use \"find\" instead of \"grep\"") | _ -> ()); mcontext_check_raw wanted_mcontext esp.mcontext let mcontext_check_unop_l wanted_mcontext esp = mcontext_check wanted_mcontext { esp with any = { esp.any with expr = List esp.any.expr } } let mcontext_check_non_none esp = if esp.mcontext = M_none then warn_rule [Warn_context] "() context not accepted here" let mcontext_check_none msg expr esp = let rec mcontext_check_none_rec msg expr = function | M_none | M_unknown -> () | M_mixed l when List.exists (fun c -> c = M_none) l -> () | M_tuple l -> (match expr with | [List l_expr] | [List l_expr ; Semi_colon] -> let rec iter = function | e::l_expr, mcontext::l -> mcontext_check_none_rec (if l = [] then msg else "value is dropped") [e] mcontext ; iter (l_expr, l) | [], [] -> () | _ -> internal_error "mcontext_check_none" in iter (un_parenthesize_full_l l_expr, l) | _ -> internal_error "mcontext_check_none") | _ -> match expr with | [List [Num("1", _)]; Semi_colon] -> () (* allow "1;" for package return value. It would be much better to check we are at toplevel, but hell i don't want to wire this information up to here *) | [List [Call_op ("<>", [Ident (None, "STDIN", _)], _)]; Semi_colon] -> () (* allow to ask "press return" *) | [List [Call(Deref(I_func, Ident(None, "map", _)), _)]; Semi_colon] -> warn_rule [Warn_void] "if you don't use the return value, use \"foreach\" instead of \"map\"" | _ -> warn [Warn_void] esp.pos msg in mcontext_check_none_rec msg expr esp.mcontext (* only returns M_float when there is at least one float *) let mcontext_float_or_int l = List.iter (mcontext_check_raw M_float) l; if List.mem M_float l then M_float else M_int let mcontext_op_assign left right = mcontext_check_non_none right; let left_mcontext = match left.mcontext with | M_mixed [ c ; M_none ] -> c | c -> c in let wanted_mcontext = match left_mcontext with | M_array -> M_list | M_hash -> M_mixed [ M_hash ; M_list ] | m -> m in mcontext_check wanted_mcontext right; let return_mcontext = match left_mcontext with | M_tuple _ -> M_array | c -> c in mcontext_merge return_mcontext M_none let mtuple_context_concat c1 c2 = match c1, c2 with | M_array, _ | _, M_array | M_hash, _ | _, M_hash -> M_list | M_tuple l, _ -> M_tuple (l @ [c2]) | _ -> M_tuple [c1 ; c2] let call_op_if_infix left right esp_start esp_end = (match left, right with | List [Call_op("=", [Deref(context, _); _], _)], _ when non_scalar_context context -> () | List [Call_op("=", [v; _], _)], List [Call_op("not", [v'], _)] when is_same_fromparser v v' -> warn_rule [Warn_suggest_simpler] "\"$foo = ... if !$foo\" can be written \"$foo ||= ...\"" | _ -> ()); mcontext_check_none "value is dropped" [left] esp_start; (match right with | List [ Num("0", _)] -> () (* allow my $x if 0 *) | _ -> check_My_under_condition "replace \"my $foo = ... if \" with \"my $foo = && ...\"" left); let pos = raw_pos_range esp_start esp_end in new_any M_none (Call_op("if infix", [ left ; right], raw_pos2pos pos)) esp_start.spaces pos let call_op_unless_infix left right esp_start esp_end = (match left, right with | List [Call_op("=", [Deref(context, _); _], _)], _ when non_scalar_context context -> () | List [Call_op("=", [v; _], _)], List [v'] when is_same_fromparser v v' -> warn_rule [Warn_suggest_simpler] "\"$foo = ... unless $foo\" can be written \"$foo ||= ...\"" | _ -> ()); (match right with | List [Call_op(op, _, _)] -> (match op with | "&&" | "||" | "not" | "ne" | "?:" -> warn_rule [Warn_complex_expressions] "don't use \"unless\" when the condition is complex, use \"if\" instead" | _ -> ()); | _ -> ()); mcontext_check_none "value is dropped" [left] esp_start; check_My_under_condition "replace \"my $foo = ... unless \" with \"my $foo = ! && ...\"" left; let pos = raw_pos_range esp_start esp_end in new_any M_none (Call_op("unless infix", [ left ; right], raw_pos2pos pos)) esp_start.spaces pos let symops pri para_context return_context op_str left op right = sp_same op right; let skip_context_check = (op_str = "==" || op_str = "!=") && (match left.any.expr, right.any.expr with | Deref(I_array, _), List [] -> true (* allow @l == () and @l != () *) | _ -> false) in if op_str <> "==" && op_str <> "!=" && para_context = M_float then (match un_parenthesize_full left.any.expr with | Call_op("last_array_index", _, _) -> warn_rule [Warn_complex_expressions] "change your expression to use @xxx instead of $#xxx" | _ -> ()); if not skip_context_check then (mcontext_check para_context left ; mcontext_check para_context right) ; to_Call_op_ return_context pri op_str [prio_lo pri left; prio_lo_after pri right] left right > 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631
# DrakX-fi - Finnish Translation
#
# Copyright (C) 2002,2003, 2004, 2005, 2006 Free Software Foundation, Inc.
# Copyright (C) 2002 Mandriva
#
#
# Matias Griese <mahagr@utu.fi>, 2001.
# Taisto Kuikka <69319@batman.jypoly.fi>, 2003,2004.
# Esa Linna <denzo@mbnet.fi>, 2004.
# Thomas Backlund <tmb@mandrake.org>, 2002, 2003, 2004, 2005.
# Thomas Backlund <tmb@mandriva.org>, 2006.
msgid ""
msgstr ""
"Project-Id-Version: DrakX-fi - Mandriva 2007 Release\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-09-25 20:13+0200\n"
"PO-Revision-Date: 2006-09-18 20:02+0300\n"
"Last-Translator: Thomas Backlund <tmb@mandriva.org>\n"
"Language-Team: Finnish <fi@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: KBabel 1.11.4\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#: any.pm:159
#, c-format
msgid "Do you have further supplementary media?"
msgstr "Onko sinulla muita asennuksen lisälevyä?"

#. -PO: keep the double empty lines between sections, this is formatted a la LaTeX
#: any.pm:162
#, c-format
msgid ""
"The following media have been found and will be used during install: %s.\n"
"\n"
"\n"
"Do you have a supplementary installation medium to configure?"
msgstr ""
"Seuraavat lähteet on löydetty ja käytetään asennuksen aikana: %s.\n"
"\n"
"\n"
"Onlo sinulla lisää lähteitä jotta haluat määrittää?"

#: any.pm:170
#, c-format
msgid "CD-ROM"
msgstr "CD-ROM"

#: any.pm:171
#, c-format
msgid "Network (HTTP)"
msgstr "Verkko (HTTP)"

#: any.pm:172
#, c-format
msgid "Network (FTP)"
msgstr "Verkko (FTP)"

#: any.pm:173
#, c-format
msgid "Network (NFS)"
msgstr "Verkko (NFS)"

#: any.pm:215
#, c-format
msgid "URL of the mirror?"
msgstr "Peilipalvelimen URL?"

#: any.pm:221
#, c-format
msgid "URL must start with ftp:// or http://"
msgstr "URL pitää alkaa ftp:// tai http://"

#: any.pm:232
#, c-format
msgid ""
"Contacting Mandriva Linux web site to get the list of available mirrors..."
msgstr ""
"Otetaan yhteys Mandriva Linuxin sivustolle peilipalvelinlistan saamiseksi..."

#: any.pm:237
#, c-format
msgid ""
"Failed contacting Mandriva Linux web site to get the list of available "
"mirrors"
msgstr ""
"Yhteydenotto Mandriva Linuxin sivustolle peilipalvelinlistan saamiseksi "
"epäonnistui"

#: any.pm:247
#, c-format
msgid "Choose a mirror from which to get the packages"
msgstr "Valitse peilipalvelin josta paketit haetaan"

#: any.pm:277
#, c-format
msgid "NFS setup"
msgstr "NFS Asetus"

#: any.pm:278
#, c-format
msgid "Please enter the hostname and directory of your NFS media"
msgstr "Syötä NFS-palvelimesi nimi ja hakemistopolku"

#: any.pm:282
#, c-format
msgid "Hostname missing"
msgstr ""

#: any.pm:283
#, c-format
msgid "Directory must begin with \"/\""
msgstr ""

#: any.pm:287
#, c-format
msgid "Hostname of the NFS mount ?"
msgstr "NFS-liitoksen palvelimen nimi?"

#: any.pm:288
#, c-format
msgid "Directory"
msgstr "Kansio"

#: any.pm:310
#, c-format
msgid "Supplementary"
msgstr ""

#: any.pm:345
#, c-format
msgid ""
"Can't find a package list file on this mirror. Make sure the location is "
"correct."
msgstr "En löydä hdlist tiedostoa tässä peilipalvelimessa."

#: any.pm:379
#, c-format
msgid "Looking at packages already installed..."
msgstr "Etsitään jo asennettuja paketteja..."

#: any.pm:386
#, c-format
msgid "Removing packages prior to upgrade..."
msgstr "Poistetaan paketteja ennen päivitystä..."

#: any.pm:428
#, c-format
msgid "Finding packages to upgrade..."
msgstr "Etsitään päivitettäviä paketteja..."

#. -PO: keep the double empty lines between sections, this is formatted a la LaTeX
#: any.pm:614
#, c-format
msgid ""
"You have selected the following server(s): %s\n"
"\n"
"\n"
"These servers are activated by default. They do not have any known security\n"
"issues, but some new ones could be found. In that case, you must make sure\n"
"to upgrade as soon as possible.\n"
"\n"
"\n"
"Do you really want to install these servers?\n"
msgstr ""
"Olet valinnut seuraavat palvelimet: %s\n"
"\n"
"\n"
"Nämä palvelimet otetaan oletuksena käyttöön. Niissä ei ole tunnettuja \n"
"turvallisuusaukkoja, mutta sellaisia voi löytyä ajan mittaan. Mikäli niin \n"
"tapahtuu, sinun pitäisi päivittää kyseiset palvelimet niin nopeasti kuin \n"
"suinkin mahdollista.\n"
"\n"
"\n"
"Haluatko todella asentaa nämä palvelimet?\n"

#. -PO: keep the double empty lines between sections, this is formatted a la LaTeX
#: any.pm:637
#, c-format
msgid ""
"The following packages will be removed to allow upgrading your system: %s\n"
"\n"
"\n"
"Do you really want to remove these packages?\n"
msgstr ""
"Seuraavat paketit poistetaan jotta järjestelmäsi voidaan päivittää:\n"
"%s\n"
"\n"
"Haluatko varmasti poistaa nämä paketit?\n"

#: any.pm:1059
#, c-format
msgid "The following disk(s) were renamed:"
msgstr "Seuraava(t) levy(t) uudelleennimettiin:"

#: any.pm:1061
#, c-format
msgid "%s (previously named as %s)"
msgstr "%s (oli ennen nimetty %s)"

#: any.pm:1118
#, c-format
msgid "HTTP"
msgstr "HTTP"

#: any.pm:1118
#, c-format
msgid "FTP"
msgstr "FTP"

#: any.pm:1118
#, c-format
msgid "NFS"
msgstr "NFS"

#: any.pm:1137 steps_interactive.pm:846
#, c-format
msgid "Network"
msgstr "Verkko"

#: any.pm:1141
#, c-format
msgid "Please choose a media"
msgstr "Ole hyvä ja valitse media"

#: any.pm:1157
#, c-format
msgid "File already exists. Overwrite it?"
msgstr "Tiedosto jo olemassa. Korvaa?"

#: any.pm:1161
#, c-format
msgid "Permission denied"
msgstr "Ei oikeuksia"

#: any.pm:1209
#, c-format
msgid "Bad NFS name"
msgstr "Virheellinen NFS-nimi"

#: any.pm:1230
#, c-format
msgid "Bad media %s"
msgstr "Huono media %s"

#: any.pm:1272
#, c-format
msgid "Can not make screenshots before partitioning"
msgstr "Kuvakaappauksia ei voida tehdä ennen osiointia"

#: any.pm:1279
#, c-format
msgid "Screenshots will be available after install in %s"
msgstr "Kuvakaappaukset löytyvät asennuksen jälkeen hakemistosta %s"

#: gtk.pm:136
#, c-format
msgid "Installation"
msgstr "Asennus"

#: gtk.pm:139 share/meta-task/compssUsers.pl:54
#, c-format
msgid "Configuration"
msgstr "Asetustyökalut"

#: install2.pm:165
#, c-format
msgid "You must also format %s"
msgstr "Sinun täytyy myös alustaa %s"

#: interactive.pm:16
#, c-format
msgid ""
"Some hardware on your computer needs ``proprietary'' drivers to work.\n"
"You can find some information about them at: %s"
msgstr ""
"Jokin osa laitteistoasi tarvitsee laitteistovalmistajan ajurit toimiakseen\n"
"kunnolla. Löydät lisätietoja edellä mainituista täältä: %s"

#: interactive.pm:22
#, c-format
msgid "Bringing up the network"
msgstr "Käynnistetään verkkoa"

#: interactive.pm:27
#, c-format
msgid "Bringing down the network"
msgstr "Ajetaan alas verkkoa"

#: media.pm:699 media.pm:710
#, c-format
msgid "Downloading file %s..."
msgstr "Haetaan tiedosto %s ..."

#: media.pm:804
#, c-format
msgid "Copying some packages on disks for future use"
msgstr "Kopioin paketteja kovalevylle myöhempään käyttöön"

#: media.pm:857
#, c-format
msgid "Copying in progress"
msgstr "Kopiointi käynnissä"

#: pkgs.pm:33
#, c-format
msgid "must have"
msgstr "pakollinen"

#: pkgs.pm:34
#, c-format
msgid "important"
msgstr "tärkeä"

#: pkgs.pm:35
#, c-format
msgid "very nice"
msgstr "erittäin hyvä"

#: pkgs.pm:36
#, c-format
msgid "nice"
msgstr "hyvä"

#: pkgs.pm:37
#, c-format
msgid "maybe"
msgstr "ehkä"

#: share/meta-task/compssUsers.pl:23
#, c-format
msgid "Workstation"
msgstr "Työasema"

#: share/meta-task/compssUsers.pl:25
#, c-format
msgid "Office Workstation"
msgstr "Toimistotyöasema"

#: share/meta-task/compssUsers.pl:27
#, c-format
msgid ""
"Office programs: wordprocessors (OpenOffice.org Writer, Kword), spreadsheets "
"(OpenOffice.org Calc, Kspread), PDF viewers, etc"
msgstr ""
"Toimisto-ohjelmistot: tekstinkäsittely (OpenOffice.org Writer, Kword), "
"taulukkolaskenta (OpenOffice.org calc, Kspread), PDF-lukijat jne"

#: share/meta-task/compssUsers.pl:28
#, c-format
msgid ""
"Office programs: wordprocessors (kword, abiword), spreadsheets (kspread, "
"gnumeric), pdf viewers, etc"
msgstr ""
"Toimisto-ohjelmistot: tekstinkäsittely (kword, abiword), taulukkolaskenta "
"(kspread, gnumeric), pdf-lukijat jne"

#: share/meta-task/compssUsers.pl:33
#, c-format
msgid "Game station"
msgstr "Peliasema"

#: share/meta-task/compssUsers.pl:34
#, c-format
msgid "Amusement programs: arcade, boards, strategy, etc"
msgstr "Viihdeohjelmat: tasohyppely, korttipelit, strategia jne"

#: share/meta-task/compssUsers.pl:37
#, c-format
msgid "Multimedia station"
msgstr "Multimediatyöasema"

#: share/meta-task/compssUsers.pl:38
#, c-format
msgid "Sound and video playing/editing programs"
msgstr "Äänen sekä videon toisto- ja editointiohjelmat"

#: share/meta-task/compssUsers.pl:43
#, c-format
msgid "Internet station"
msgstr "Internettyöasema"

#: share/meta-task/compssUsers.pl:44
#, c-format
msgid ""
"Set of tools to read and send mail and news (mutt, tin..) and to browse the "
"Web"
msgstr ""
"Valikoima työkaluja sähköpostin ja uutisryhmien lukemiseen (mutt, tin...) "
"sekä Internetin selailuun"

#: share/meta-task/compssUsers.pl:49
#, c-format
msgid "Network Computer (client)"
msgstr "Verkkopääte (asiakas)"

#: share/meta-task/compssUsers.pl:50
#, c-format
msgid "Clients for different protocols including ssh"
msgstr "Asiakasohjelmat eri protokollille (SSH jne)"

#: share/meta-task/compssUsers.pl:55
#, c-format
msgid "Tools to ease the configuration of your computer"
msgstr "Työkalut, jotka helpottavat tietokoneesi asetusten muokkaamista"

#: share/meta-task/compssUsers.pl:59
#, c-format
msgid "Console Tools"
msgstr "Komentorivityökalut"

#: share/meta-task/compssUsers.pl:60
#, c-format
msgid "Editors, shells, file tools, terminals"
msgstr "Editorit, komentotulkit, tiedostotyökalut, päätteet"

#: share/meta-task/compssUsers.pl:64 share/meta-task/compssUsers.pl:166
#: share/meta-task/compssUsers.pl:168
#, c-format
msgid "Development"
msgstr "Kehitysympäristö"

#: share/meta-task/compssUsers.pl:65 share/meta-task/compssUsers.pl:169
#, c-format
msgid "C and C++ development libraries, programs and include files"
msgstr "C ja C++ kehityskirjastot, ohjelmat ja include-tiedostot"

#: share/meta-task/compssUsers.pl:69 share/meta-task/compssUsers.pl:173
#, c-format
msgid "Documentation"
msgstr "Dokumentaatio"

#: share/meta-task/compssUsers.pl:70 share/meta-task/compssUsers.pl:174
#, c-format
msgid "Books and Howto's on Linux and Free Software"
msgstr "Kirjoja ja ohjeita Linuxista sekä vapaan lähdekoodin ohjelmista"

#: share/meta-task/compssUsers.pl:74 share/meta-task/compssUsers.pl:177
#, c-format
msgid "LSB"
msgstr "LSB"

#: share/meta-task/compssUsers.pl:75 share/meta-task/compssUsers.pl:178
#, c-format
msgid "Linux Standard Base. Third party applications support"
msgstr ""
"Linux Standard Base (Linux Standardi Perusta)\n"
" - Kolmannen osapuolen ohjelmistojen tuki"

#: share/meta-task/compssUsers.pl:84
#, c-format
msgid "Web Server"
msgstr "WWW-palvelin"

#: share/meta-task/compssUsers.pl:85
#, c-format
msgid "Apache"
msgstr "Apache"

#: share/meta-task/compssUsers.pl:88
#, c-format
msgid "Groupware"
msgstr "Groupware"

#: share/meta-task/compssUsers.pl:89
#, c-format
msgid "Kolab Server"
msgstr "Kolab palvelin"

#: share/meta-task/compssUsers.pl:92 share/meta-task/compssUsers.pl:133
#, c-format
msgid "Firewall/Router"
msgstr "Palomuuri / Reititin"

#: share/meta-task/compssUsers.pl:93 share/meta-task/compssUsers.pl:134
#, c-format
msgid "Internet gateway"
msgstr "Internetin yhdyskäytävä"

#: share/meta-task/compssUsers.pl:96
#, c-format
msgid "Mail/News"
msgstr "Posti/Uutiset"

#: share/meta-task/compssUsers.pl:97
#, c-format
msgid "Postfix mail server, Inn news server"
msgstr "Postfix sähköpostipalvelin, Inn uutispalvelin"

#: share/meta-task/compssUsers.pl:100
#, c-format
msgid "Directory Server"
msgstr "Hakemisto-palvelin"

#: share/meta-task/compssUsers.pl:104
#, c-format
msgid "FTP Server"
msgstr "FTP-palvelin"

#: share/meta-task/compssUsers.pl:105
#, c-format
msgid "ProFTPd"
msgstr "ProFTPd"

#: share/meta-task/compssUsers.pl:108
#, c-format
msgid "DNS/NIS"
msgstr "DNS/NIS"

#: share/meta-task/compssUsers.pl:109
#, c-format
msgid "Domain Name and Network Information Server"
msgstr "Verkkoalueen nimipalvelin (DNS) ja verkon informaatio-palvelin"

#: share/meta-task/compssUsers.pl:112
#, c-format
msgid "File and Printer Sharing Server"
msgstr "Tiedosto- ja Tulostuspalvelin"

#: share/meta-task/compssUsers.pl:113
#, c-format
msgid "NFS Server, Samba server"
msgstr "NFS-palvelin, Samba-palvelin"

#: share/meta-task/compssUsers.pl:116 share/meta-task/compssUsers.pl:129
#, c-format
msgid "Database"
msgstr "Tietokanta"

#: share/meta-task/compssUsers.pl:117
#, c-format
msgid "PostgreSQL and MySQL Database Server"
msgstr "PostgreSQL ja MySQL tietokantapalvelin"

#: share/meta-task/compssUsers.pl:121
#, c-format
msgid "Web/FTP"
msgstr "Web/FTP"

#: share/meta-task/compssUsers.pl:122
#, c-format
msgid "Apache, Pro-ftpd"
msgstr "Apache WWW-palvelin ja Pro-ftpd FTP-palvelin"

#: share/meta-task/compssUsers.pl:125
#, c-format
msgid "Mail"
msgstr "Sähköposti"

#: share/meta-task/compssUsers.pl:126
#, c-format
msgid "Postfix mail server"
msgstr "Postfix sähköpostipalvelin"

#: share/meta-task/compssUsers.pl:130
#, c-format
msgid "PostgreSQL or MySQL database server"
msgstr "PostgreSQL tai MySQL tietokantapalvelin"

#: share/meta-task/compssUsers.pl:137
#, c-format
msgid "Network Computer server"
msgstr "Verkkotietokone (palvelin)"

#: share/meta-task/compssUsers.pl:138
#, c-format
msgid "NFS server, SMB server, Proxy server, ssh server"
msgstr "NFS-, SMB-, välitys- ja SSH-palvelin"

#: share/meta-task/compssUsers.pl:144
#, c-format
msgid "Graphical Environment"
msgstr "Graafinen ympäristö"

#: share/meta-task/compssUsers.pl:146
#, c-format
msgid "KDE Workstation"
msgstr "KDE-työasema"

#: share/meta-task/compssUsers.pl:147
#, c-format
msgid ""
"The K Desktop Environment, the basic graphical environment with a collection "
"of accompanying tools"
msgstr ""
"K-työpöytäympäristö. Graafinen perusympäristö ja sen mukana tulevat työkalut"

#: share/meta-task/compssUsers.pl:151
#, c-format
msgid "GNOME Workstation"
msgstr "GNOME-työasema"

#: share/meta-task/compssUsers.pl:152
#, c-format
msgid ""
"A graphical environment with user-friendly set of applications and desktop "
"tools"
msgstr ""
"Graafinen ympäristö käyttäjäystävällisellä sovelluksilla ja työkaluilla."

#: share/meta-task/compssUsers.pl:155
#, c-format
msgid "IceWm Desktop"
msgstr "IceWm Työpöytä"

#: share/meta-task/compssUsers.pl:159
#, c-format
msgid "Other Graphical Desktops"
msgstr "Muut graafiset käyttöympäristöt"

#: share/meta-task/compssUsers.pl:160
#, c-format
msgid "Window Maker, Enlightenment, Fvwm, etc"
msgstr "Window Maker, Enlightenment, FVWM jne"

#: share/meta-task/compssUsers.pl:183
#, c-format
msgid "Utilities"
msgstr "Työkalut"

#: share/meta-task/compssUsers.pl:185 share/meta-task/compssUsers.pl:186
#, c-format
msgid "SSH Server"
msgstr "SSH-palvelin"

#: share/meta-task/compssUsers.pl:190
#, c-format
msgid "Webmin"
msgstr "Webmin"

#: share/meta-task/compssUsers.pl:191
#, c-format
msgid "Webmin Remote Configuration Server"
msgstr "Webmin Etäasetuspalvelin"

#: share/meta-task/compssUsers.pl:195
#, c-format
msgid "Network Utilities/Monitoring"
msgstr "Verkkotyökalut/Valvonta"

#: share/meta-task/compssUsers.pl:196
#, c-format
msgid "Monitoring tools, processes accounting, tcpdump, nmap, ..."
msgstr "Valvontatyökalut, prosessihallinta, tcpdump, nmap, ..."

#: share/meta-task/compssUsers.pl:200
#, c-format
msgid "Mandriva Wizards"
msgstr "Mandriva Velhoja"

#: share/meta-task/compssUsers.pl:201
#, c-format
msgid "Wizards to configure server"
msgstr "Velhoja palvelimen asettamiseksi"

#: steps.pm:85
#, c-format
msgid ""
"An error occurred, but I do not know how to handle it nicely.\n"
"Continue at your own risk."
msgstr ""
"Ilmeni virhe, eikä sitä ei voida käsitellä kunnolla.\n"
"Jatka omalla vastuullasi."

#: steps.pm:432
#, c-format
msgid ""
"Some important packages did not get installed properly.\n"
"Either your cdrom drive or your cdrom is defective.\n"
"Check the cdrom on an installed computer using \"rpm -qpl media/main/*.rpm"
"\"\n"
msgstr ""
"Osa tärkeistä paketeista ei asentunut kunnolla.\n"
"Joko CD-ROM-asemasi tai levy on viallinen.\n"
"Tarkista CD-ROM Linux-koneessa komennolla \"rpm -qpl media/main/*.rpm\"\n"

#: steps_auto_install.pm:71 steps_stdio.pm:27
#, c-format
msgid "Entering step `%s'\n"
msgstr "Siirryn vaiheeseen `%s'\n"

#: steps_curses.pm:22
#, c-format
msgid "Mandriva Linux Installation %s"
msgstr "Mandriva Linux asennus %s"

#: steps_curses.pm:32
#, c-format
msgid "<Tab>/<Alt-Tab> between elements"
msgstr "<Tab>/<Alt-Tab> vaihtaa elementtiä"

#: steps_gtk.pm:155 steps_gtk.pm:156
#, c-format
msgid "Localization"
msgstr ""

#: steps_gtk.pm:159 steps_list.pm:20
#, c-format
msgid ""
"_: Keep these entry short\n"
"Installation class"
msgstr "Asennusluokka"

#: steps_gtk.pm:160 steps_gtk.pm:243 steps_interactive.pm:511
#, c-format
msgid "Package Group Selection"
msgstr "Pakettiryhmien valinta"

#: steps_gtk.pm:161 steps_gtk.pm:460 steps_interactive.pm:543
#, c-format
msgid "Installing"
msgstr "Asennetaan"

# Asennuksen sivuvalikko
#: steps_gtk.pm:162 steps_gtk.pm:576 steps_interactive.pm:735
#, c-format
msgid "Summary"
msgstr "Yhteenveto"

#: steps_gtk.pm:164 steps_gtk.pm:165 steps_list.pm:30
#, c-format
msgid ""
"_: Keep these entry short\n"
"Bootloader"
msgstr "Käynnistyslataaja"

#: steps_gtk.pm:166 steps_interactive.pm:644
#, c-format
msgid "Updates"
msgstr "Päivitykset"

#: steps_gtk.pm:213
#, c-format
msgid ""
"Your system is low on resources. You may have some problem installing\n"
"Mandriva Linux. If that occurs, you can try a text install instead. For "
"this,\n"
"press `F1' when booting on CDROM, then enter `text'."
msgstr ""
"Järjestelmäsi resurssit ovat lopussa. Voit kohdata ongelmia Mandriva "
"Linuxia\n"
"asentaessasi. Jos näin tapahtuu, voit kokeilla tekstipohjaista asennusta.\n"
"Tehdäksesi niin paina `F1' kun käynnistät asennusohjelmaa CD-ROM:lta.\n"
"Tämän jälkeen kirjoita `text'."

#: steps_gtk.pm:264 steps_interactive.pm:529
#, c-format
msgid "Individual package selection"
msgstr "Yksittäisten pakettien valinta"

# mat
#: steps_gtk.pm:268 steps_interactive.pm:454
#, c-format
msgid "Total size: %d / %d MB"
msgstr "Koko yhteensä: %d / %d Mt"

#: steps_gtk.pm:313
#, c-format
msgid "Bad package"
msgstr "Viallinen paketti"

#: steps_gtk.pm:315
#, c-format
msgid "Version: "
msgstr "Versio: "

# mat
#: steps_gtk.pm:316
#, c-format
msgid "Size: "
msgstr "Koko: "

#: steps_gtk.pm:316
#, c-format
msgid "%d KB\n"
msgstr "%d kt\n"

#: steps_gtk.pm:317
#, c-format
msgid "Importance: "
msgstr "Tärkeys: "

#: steps_gtk.pm:351
#, c-format
msgid "You can not select/unselect this package"
msgstr "Et voi valita/poistaa tätä pakettia"

#: steps_gtk.pm:355
#, c-format
msgid "due to missing %s"
msgstr "puuttuvista %s johtuen"

#: steps_gtk.pm:356
#, c-format
msgid "due to unsatisfied %s"
msgstr "täyttämättömistä riippuvuuksista %s johtuen"

#: steps_gtk.pm:357
#, c-format
msgid "trying to promote %s"
msgstr "yritetään asentaa %s"

#: steps_gtk.pm:358
#, c-format
msgid "in order to keep %s"
msgstr "jotta %s voitaisiin säilyttää"

#: steps_gtk.pm:363
#, c-format
msgid ""
"You can not select this package as there is not enough space left to install "
"it"
msgstr ""
"Et voi asentaa tätä pakettia, koska levyllä ei ole tarpeeksi tilaa sen "
"asentamiseksi"

#: steps_gtk.pm:366
#, c-format
msgid "The following packages are going to be installed"
msgstr "Seuraavat paketit asennetaan"

#: steps_gtk.pm:367
#, c-format
msgid "The following packages are going to be removed"
msgstr "Seuraavat paketit poistetaan"

#: steps_gtk.pm:392
#, c-format
msgid "This is a mandatory package, it can not be unselected"
msgstr "Tämä on pakollinen paketti, sitä ei voida poistaa valinnoista"

#: steps_gtk.pm:394
#, c-format
msgid "You can not unselect this package. It is already installed"
msgstr "Et voi poistaa tämän paketin valintaa. Se on jo asennettu"

#: steps_gtk.pm:396
#, c-format
msgid "You can not unselect this package. It must be upgraded"
msgstr "Et voi poistaa tämän paketin valintaa. Paketti pitää päivittää."

#: steps_gtk.pm:400
#, c-format
msgid "Show automatically selected packages"
msgstr "Näytä automaattisesti valitut paketit"

#: steps_gtk.pm:402 steps_interactive.pm:133
#, c-format
msgid "Install"
msgstr "Asenna"

#: steps_gtk.pm:405
#, c-format
msgid "Load/Save selection"
msgstr "Lataa/Tallenna valinta"

#: steps_gtk.pm:406
#, c-format
msgid "Updating package selection"
msgstr "Päivitän pakettien valintaa"

# Asennuksen sivuvalikko
#: steps_gtk.pm:411
#, c-format
msgid "Minimal install"
msgstr "Minimaalinen asennus"

#: steps_gtk.pm:425
#, c-format
msgid "Software Management"
msgstr "Ohjelmistohallinta"

#: steps_gtk.pm:425 steps_interactive.pm:373
#, c-format
msgid "Choose the packages you want to install"
msgstr "Valitse asennettavat paketit"

#: steps_gtk.pm:487
#, c-format
msgid "No details"
msgstr "Ei yksityiskohtia"

#: steps_gtk.pm:483
#, c-format
msgid "Time remaining "
msgstr "Aikaa jäljellä "

#: steps_gtk.pm:484
#, c-format
msgid "Estimating"
msgstr "Arvioidaan aikaa"

#: steps_gtk.pm:511
#, fuzzy, c-format
msgid "%d package"
msgid_plural "%d packages"
msgstr[0] "%d pakettia"
msgstr[1] "%d pakettia"

#: steps_gtk.pm:589
#, c-format
msgid "Configure"
msgstr "Aseta"

#: steps_gtk.pm:580 steps_interactive.pm:731 steps_interactive.pm:858
#, c-format
msgid "not configured"
msgstr "ei asetettu"

#: steps_gtk.pm:612 steps_interactive.pm:268
#, c-format
msgid ""
"The following installation media have been found.\n"
"If you want to skip some of them, you can unselect them now."
msgstr ""
"Seuraavar asennusmediat on löydetty.\n"
"Jos haluat ohittaa joku niistä, voit poistaa niiden valinta nyt."

#: steps_gtk.pm:621 steps_interactive.pm:274
#, c-format
msgid ""
"You have the option to copy the contents of the CDs onto the hard drive "
"before installation.\n"
"It will then continue from the hard drive and the packages will remain "
"available once the system is fully installed."
msgstr ""
"Sinulla on mahdollisuus kopioida CD-levyjen sisältö kovalevylle ennen "
"asennusta.\n"
"Asennus jatkaa sen jälkeen kovalevyltä ja paketit ovat käytettävissä myös "
"asennuksen jälkeen."

#: steps_gtk.pm:623 steps_interactive.pm:276
#, c-format
msgid "Copy whole CDs"
msgstr "Kopioi koko CD"

#: steps_interactive.pm:38
#, c-format
msgid "An error occurred"
msgstr "Ilmeni virhe"

#: steps_interactive.pm:97
#, c-format
msgid "Please choose your keyboard layout."
msgstr "Valitse näppäimistösi asettelu."

#: steps_interactive.pm:99
#, c-format
msgid "Here is the full list of available keyboards"
msgstr "Tässä on koko lista käytettävissä olevista näppäimistöistä"

#: steps_interactive.pm:128
#, c-format
msgid "Install/Upgrade"
msgstr "Asenna/Päivitä"

#: steps_interactive.pm:129
#, c-format
msgid "Is this an install or an upgrade?"
msgstr "Onko tämä asennus vai päivitys?"

#: steps_interactive.pm:134
#, fuzzy, c-format
msgid ""
"_: This is a noun:\n"
"Install"
msgstr "Asenna"

#: steps_interactive.pm:137
#, c-format
msgid "Upgrade %s"
msgstr "Päivitä %s"

#: steps_interactive.pm:148
#, c-format
msgid "Encryption key for %s"
msgstr "Salausavain %s:lle"

#: steps_interactive.pm:184
#, c-format
msgid "IDE"
msgstr "IDE"

#: steps_interactive.pm:184
#, c-format
msgid "Configuring IDE"
msgstr "Asetetaan IDE-levyä"

#: steps_interactive.pm:221
#, c-format
msgid ""
"No free space for 1MB bootstrap! Install will continue, but to boot your "
"system, you'll need to create the bootstrap partition in DiskDrake"
msgstr ""
"Ei vapaata tilaa 1 Mt:n käynnistyslohkoa varten! Asennusta jatketaan, mutta "
"käynnistääksesi järjestelmän sinun pitää luoda käynnistyslohko-osio "
"DiskDrake:ssa."

#: steps_interactive.pm:226
#, c-format
msgid ""
"You'll need to create a PPC PReP Boot bootstrap! Install will continue, but "
"to boot your system, you'll need to create the bootstrap partition in "
"DiskDrake"
msgstr ""
"Sinun pitää luoda PPC PReP Boot bootstrap! Asennusta jatketaan, mutta "
"käynnistääksesi järjestelmän sinun pitää luoda käynnistyslohko-osio "
"DiskDrake:ssa."

#: steps_interactive.pm:318
#, c-format
msgid ""
"Change your Cd-Rom!\n"
"Please insert the Cd-Rom labelled \"%s\" in your drive and press Ok when "
"done.\n"
"If you do not have it, press Cancel to avoid installation from this Cd-Rom."
msgstr ""
"Vaihda CD-levyä!\n"
"\n"
"Aseta CD-levy nimeltä \"%s\" CD-asemaan ja valitse OK kun olet valmis.\n"
"Jos sinulla ei ole levyä, valitse Peruuta välttääksesi asennukset tältä "
"levyltä."

#: steps_interactive.pm:340
#, c-format
msgid "Looking for available packages..."
msgstr "Etsitään saatavilla olevia paketteja..."

#: steps_interactive.pm:349
#, c-format
msgid ""
"Your system does not have enough space left for installation or upgrade (%"
"dMB > %dMB)"
msgstr ""

#: steps_interactive.pm:385
#, c-format
msgid ""
"Please choose load or save package selection.\n"
"The format is the same as auto_install generated files."
msgstr ""
"Lataa tai tallenna pakettien valinta.\n"
"Muoto on sama kuin auto_install-toiminnon luomilla tiedostoilla."

#: steps_interactive.pm:387
#, c-format
msgid "Load"
msgstr "Verkkokuorma"

#: steps_interactive.pm:387
#, c-format
msgid "Save"
msgstr "Tallenna"

#: steps_interactive.pm:395
#, c-format
msgid "Bad file"
msgstr "Huono tiedosto"

#: steps_interactive.pm:413
#, c-format
msgid "Install Mandriva KDE Desktop"
msgstr ""

#: steps_interactive.pm:414
#, c-format
msgid "Install Mandriva GNOME Desktop"
msgstr ""

# Asennuksen sivuvalikko
#: steps_interactive.pm:415
#, fuzzy, c-format
msgid "Custom install"
msgstr "Minimaalinen asennus"

#: steps_interactive.pm:418
#, c-format
msgid "You can choose your workstation desktop profile: KDE, GNOME or Custom"
msgstr ""

#: steps_interactive.pm:503
#, c-format
msgid "Selected size is larger than available space"
msgstr "Valittu koko on suurempi kuin käytettävissä oleva levytila"

#: steps_interactive.pm:483
#, c-format
msgid "Type of install"
msgstr "Asennuksen tyyppi"

#: steps_interactive.pm:484
#, c-format
msgid ""
"You have not selected any group of packages.\n"
"Please choose the minimal installation you want:"
msgstr ""
"Et ole valinnut yhtäkään pakettiryhmää.\n"
"Valitse haluamasi minimaalinen asennus"

#: steps_interactive.pm:487
#, c-format
msgid "With X"
msgstr "X-palvelimella"

#: steps_interactive.pm:488
#, c-format
msgid "With basic documentation (recommended!)"
msgstr "Perusdokumentaation kanssa (suositeltu!)"

#: steps_interactive.pm:489
#, c-format
msgid "Truly minimal install (especially no urpmi)"
msgstr "Vähimmäisasennus (erityisesti ei urpmi)"

#: steps_interactive.pm:528
#, c-format
msgid "All"
msgstr "Kaikki"

#: steps_interactive.pm:544
#, c-format
msgid "Preparing installation"
msgstr "Valmistellaan asennusta"

#: steps_interactive.pm:552
#, c-format
msgid "Installing package %s"
msgstr "Asennetaan pakettia: %s"

#: steps_interactive.pm:576
#, c-format
msgid "There was an error ordering packages:"
msgstr "Tapahtui virhe järjestettäessä paketteja:"

#: steps_interactive.pm:576
#, c-format
msgid "Go on anyway?"
msgstr "Jatka kuitenkin?"

#: steps_interactive.pm:580
#, c-format
msgid "Retry"
msgstr ""

#: steps_interactive.pm:581
#, c-format
msgid "Skip this package"
msgstr ""

#: steps_interactive.pm:582
#, c-format
msgid "Skip all packages from medium \"%s\""
msgstr ""

#: steps_interactive.pm:583
#, fuzzy, c-format
msgid "Go back to media and packages selection"
msgstr "Tallenna pakettien valinta"

#: steps_interactive.pm:586
#, fuzzy, c-format
msgid "There was an error installing package %s."
msgstr "Tapahtui virhe asennettaessa paketteja:"

#: steps_interactive.pm:604
#, c-format
msgid "Post-install configuration"
msgstr "Asennuksen jälkeiset toiminnot"

#: steps_interactive.pm:611
#, c-format
msgid "Please ensure the Update Modules media is in drive %s"
msgstr "Varmista että Päivitetyt Moduulit media on asemassa %s"

#: steps_interactive.pm:645
#, c-format
msgid ""
"You now have the opportunity to download updated packages. These packages\n"
"have been updated after the distribution was released. They may\n"
"contain security or bug fixes.\n"
"\n"
"To download these packages, you will need to have a working Internet \n"
"connection.\n"
"\n"
"Do you want to install the updates?"
msgstr ""
"Sinulla on nyt mahdollisuus hakea päivitetyt paketit. Nämä paketit\n"
"on julkaistu tämän jakelun julkaisun jälkeen. Ne voivat sisältää\n"
"tietoturva- tai virhekorjauksia.\n"
"\n"
"Hakeaksesi nämä paketit sinulla pitää olla toimiva Internetyhteys.\n"
"\n"
"Haluatko hakea ja asentaa päivitykset?"

#: steps_interactive.pm:666
#, c-format
msgid "Contacting the mirror to get the list of available packages..."
msgstr "Yhdistetään peilipalvelimeen uusimman pakettilistan saamiseksi..."

#: steps_interactive.pm:672
#, c-format
msgid "Unable to contact mirror %s"
msgstr "Yhteyttä peilipalvelimeen %s ei voitu muodostaa"

#. -PO: example: lilo-graphic on /dev/hda1
#: steps_interactive.pm:777
#, c-format
msgid "%s on %s"
msgstr "%s kohteessa %s"

#: steps_interactive.pm:810 steps_interactive.pm:817 steps_interactive.pm:830
#: steps_interactive.pm:847 steps_interactive.pm:863 steps_interactive.pm:874
#, c-format
msgid "Hardware"
msgstr "Laitteisto"

#: steps_interactive.pm:795 steps_interactive.pm:812
#, c-format
msgid "Sound card"
msgstr "Äänikortti"

#: steps_interactive.pm:815
#, c-format
msgid "Do you have an ISA sound card?"
msgstr "Onko sinulla ISA-väylään liitettävä äänikortti?"

#: steps_interactive.pm:817
#, c-format
msgid ""
"Run \"alsaconf\" or \"sndconfig\" after installation to configure your sound "
"card"
msgstr ""
"Suorita \"alsaconf\" tai \"sndconfig\" asennuksen jälkeen asettaaksesi "
"äänikorttisi."

#: steps_interactive.pm:819
#, c-format
msgid "No sound card detected. Try \"harddrake\" after installation"
msgstr ""
"Yhtäkään äänikorttia ei löydetty. Kokeile \"harddrake\" asennuksen jälkeen"

#: steps_interactive.pm:828
#, c-format
msgid "TV card"
msgstr "TV-kortti"

#: steps_interactive.pm:839
#, c-format
msgid "Graphical interface"
msgstr "Graafinen käyttöliittymä"

#: steps_interactive.pm:845 steps_interactive.pm:856
#, c-format
msgid "Network & Internet"
msgstr "Verkko & Internet"

#: steps_interactive.pm:857
#, c-format
msgid "Proxies"
msgstr "Välityspalvelimet"

#: steps_interactive.pm:858
#, c-format
msgid "configured"
msgstr "asetettu"

#: steps_interactive.pm:868
#, c-format
msgid "Security Level"
msgstr "Turvataso"

#: steps_interactive.pm:882
#, c-format
msgid "Firewall"
msgstr "Palomuuri"

#: steps_interactive.pm:886
#, c-format
msgid "activated"
msgstr "aktivoitu"

#: steps_interactive.pm:886
#, c-format
msgid "disabled"
msgstr "ei käytössä"

#: steps_interactive.pm:939
#, c-format
msgid "You have not configured X. Are you sure you really want this?"
msgstr "Et ole asettanut X-palvelinta. Oletko varma että haluat tehdä tämän?"

#: steps_interactive.pm:973
#, c-format
msgid "Preparing bootloader..."
msgstr "Valmistelen käyttöjärjestelmän lataajaa..."

#: steps_interactive.pm:983
#, c-format
msgid ""
"You appear to have an OldWorld or Unknown machine, the yaboot bootloader "
"will not work for you. The install will continue, but you'll need to use "
"BootX or some other means to boot your machine. The kernel argument for the "
"root fs is: root=%s"
msgstr ""
"Sinulla on vanhanaikainen tai tuntematon tietokone, joten Yaboot "
"käynnistyslataaja ei toimi koneessasi. Asennus jatkuu, mutta sinun täytyy "
"käyttää BootX:ää koneesi käynnistämiseen. Ytimen paremetrit root "
"tiedostojärjestelmälle on: root=%s"

#: steps_interactive.pm:989
#, c-format
msgid "Do you want to use aboot?"
msgstr "Haluatko käyttää aboot:a?"

#: steps_interactive.pm:992
#, c-format
msgid ""
"Error installing aboot, \n"
"try to force installation even if that destroys the first partition?"
msgstr ""
"Virhe asennettaessa aboot:a,\n"
"yritetäänkö pakkoasennusta vaikka se tuhoaa ensimmäisen osion?"

#: steps_interactive.pm:1009
#, c-format
msgid ""
"In this security level, access to the files in the Windows partition is "
"restricted to the administrator."
msgstr ""
"Tällä turvatasolla pääsy Windows-osion tiedostoihin on rajoitettu "
"ylläpitäjälle."

# mat
#: steps_interactive.pm:1038
#, c-format
msgid "Insert a blank floppy in drive %s"
msgstr "Aseta tyhjä levyke levykeasemaan %s"

#: steps_interactive.pm:1040
#, c-format
msgid "Creating auto install floppy..."
msgstr "Valmistellaan automaattiasennuslevykettä..."

#: steps_interactive.pm:1051
#, c-format
msgid ""
"Some steps are not completed.\n"
"\n"
"Do you really want to quit now?"
msgstr ""
"Joitain vaiheita ei ole saatettu loppuun.\n"
"\n"
"Haluatko todella lopettaa?"

#: steps_interactive.pm:1061
#, c-format
msgid "Congratulations"
msgstr "Onnittelut"

#: steps_interactive.pm:1065
#, c-format
msgid "Reboot"
msgstr "Käynnistä uudelleen"

#: steps_interactive.pm:1069 steps_interactive.pm:1070
#, c-format
msgid "Generate auto install floppy"
msgstr "Luo automaattinen asennuslevyke"

#: steps_interactive.pm:1071
#, c-format
msgid ""
"The auto install can be fully automated if wanted,\n"
"in that case it will take over the hard drive!!\n"
"(this is meant for installing on another box).\n"
"\n"
"You may prefer to replay the installation.\n"
msgstr ""
"Automaattinen asennus voi olla täysin automatisoitu,\n"
"jos niin halutaan. Siinä tapauksessa asennus täyttää\n"
"koko kiintolevyn! (tarkoitettu toisen koneen asentamiseksi),\n"
"\n"
"Ehkä haluat mieluummin toistaa asennuksen.\n"

#: steps_interactive.pm:1076
#, c-format
msgid "Replay"
msgstr "Toista"

#: steps_interactive.pm:1076
#, c-format
msgid "Automated"
msgstr "Automaattinen"

#: steps_interactive.pm:1079
#, c-format
msgid "Save packages selection"
msgstr "Tallenna pakettien valinta"

# Asennuksen sivuvalikko
#. -PO: please keep the following messages very short: they must fit in the left list of the installer!!!
#: steps_list.pm:16
#, c-format
msgid ""
"_: Keep these entry short\n"
"Language"
msgstr "Kieli"

#: steps_list.pm:17
#, c-format
msgid ""
"_: Keep these entry short\n"
"License"
msgstr "Lisenssi"

#: steps_list.pm:18
#, c-format
msgid ""
"_: Keep these entry short\n"
"Mouse"
msgstr "Hiiri"

#: steps_list.pm:19
#, c-format
msgid ""
"_: Keep these entry short\n"
"Hard drive detection"
msgstr "Kiintolevyjen tunnistus"

#: steps_list.pm:21
#, c-format
msgid ""
"_: Keep these entry short\n"
"Keyboard"
msgstr "Näppäimistö"

#: steps_list.pm:22
#, c-format
msgid ""
"_: Keep these entry short\n"
"Security"
msgstr "Tietoturva"

#: steps_list.pm:23
#, c-format
msgid ""
"_: Keep these entry short\n"
"Partitioning"
msgstr "Osiointi"

#: steps_list.pm:24
#, c-format
msgid ""
"_: Keep these entry short\n"
"Formatting"
msgstr "Alustetaan"

#: steps_list.pm:25
#, c-format
msgid ""
"_: Keep these entry short\n"
"Choosing packages"
msgstr "Pakettien valitseminen"

#: steps_list.pm:26
#, c-format
msgid ""
"_: Keep these entry short\n"
"Installing"
msgstr "Asennetaan"

#: steps_list.pm:28
#, c-format
msgid ""
"_: Keep these entry short\n"
"Users"
msgstr "Käyttäjät"

#: steps_list.pm:29
#, c-format
msgid ""
"_: Keep these entry short\n"
"Networking"
msgstr "Verkko"

# Asennuksen sivuvalikko
#: steps_list.pm:31
#, c-format
msgid ""
"_: Keep these entry short\n"
"Configure X"
msgstr "Aseta X"

# Asennuksen sivuvalikko
#: steps_list.pm:32
#, c-format
msgid ""
"_: Keep these entry short\n"
"Summary"
msgstr "Yhteenveto"

#: steps_list.pm:33
#, c-format
msgid ""
"_: Keep these entry short\n"
"Services"
msgstr "Palvelut"

#: steps_list.pm:34
#, c-format
msgid ""
"_: Keep these entry short\n"
"Updates"
msgstr "Päivitykset"

#: steps_list.pm:35
#, c-format
msgid ""
"_: Keep these entry short\n"
"Exit"
msgstr "Poistu"

#~ msgid "Boot"
#~ msgstr "Käynnistys"

#~ msgid ""
#~ "_: Keep these entry short\n"
#~ "Authentication"
#~ msgstr "Tunnistustapa"

#~ msgid "Arkeia"
#~ msgstr "Arkeia"

#~ msgid "Flatout"
#~ msgstr "Flatout"

#~ msgid "Gwenview"
#~ msgstr "Gwenview"

#~ msgid "3D"
#~ msgstr "3D"

#~ msgid "CMS"
#~ msgstr "CMS"

#~ msgid "CRM"
#~ msgstr "CRM"

#~ msgid "2007 product line"
#~ msgstr "2007 tuotelinja"

#~ msgid "Invictus Firewall"
#~ msgstr "Invictus Palomuuri"

#~ msgid "Discovery Live Mode"
#~ msgstr "Discovery Live Tila"

#~ msgid "How to register"
#~ msgstr "Miten rekisteröityä"

#~ msgid "Rpmdrake 2"
#~ msgstr "Rpmdrake 2"

#~ msgid "Mandriva Online Services"
#~ msgstr "Mandriva Online Palvelut"

#~ msgid "New Theme"
#~ msgstr "Uusi teema"

#~ msgid "Web 2.0"
#~ msgstr "Web 2.0"

#~ msgid "Kaspersky"
#~ msgstr "Kaspersky"

#~ msgid "LinDVD"
#~ msgstr "LinDVD"

#~ msgid "Skype"
#~ msgstr "Skype"

#~ msgid "Transgaming/Cedega"
#~ msgstr "Transgaming/Cedega"

#~ msgid "DrakVPN"
#~ msgstr "DrakVPN"

#, fuzzy
#~ msgid "(%d package, %d MB)"
#~ msgid_plural "(%d packages, %d MB)"
#~ msgstr[0] "%d pakettia"
#~ msgstr[1] "%d pakettia"

#~ msgid "%d packages"
#~ msgstr "%d pakettia"

# Asennuksen sivuvalikko
#~ msgid "Language"
#~ msgstr "Kieli"

#~ msgid "License"
#~ msgstr "Lisenssi"

#~ msgid "Installation class"
#~ msgstr "Asennusluokka"

#~ msgid "Formatting"
#~ msgstr "Alustetaan"

#~ msgid "Choosing packages"
#~ msgstr "Pakettien valitseminen"

#~ msgid "Users"
#~ msgstr "Käyttäjät"

#~ msgid "Networking"
#~ msgstr "Verkko"

# Asennuksen sivuvalikko
#~ msgid "Configure X"
#~ msgstr "Aseta X"

#~ msgid ""
#~ "Can not access kernel modules corresponding to your kernel (file %s is "
#~ "missing), this generally means your boot floppy in not in sync with the "
#~ "Installation medium (please create a newer boot floppy)"
#~ msgstr ""
#~ "Käyttämääsi ydintä vastaavia ytimen moduuleja ei voida käyttää (tiedosto %"