[CrackMonkey] uuencode in awk!

Joey Hess joey at kitenet.net
Thu Jul 27 03:12:33 PDT 2000


Nick Moffitt wrote:
> It has uudecode in awk!

Incomplete debconf inplementation in awk (and it'll be finished over my
dead body (if you don't know what debconf is, go away)):

#!/usr/bin/mawk -f
#
# Nanoconf, a tiny debconf-compatible configuration system.
#
# Copyright (C) 2000  Massimo Dal Zotto <dz at cs.unitn.it>

BEGIN {
    NANOCONF_VERSION = "0.1"
    PROTOCOL_VERSION = "2.0"
    STDERR           = "/dev/stderr"
    MBOX	     = or(env("MAILBOX"), env("HOME")"/nanoconf-messages")
    TEMPFILE         = or(env("TMP"),"/tmp") "/nanoconf" getpid() rand()
    TTY		     = or(env("NANOCONF_TTY"), "/dev/tty")

    debug(1,"# nanoconf version "NANOCONF_VERSION)

    make_array(flag_keywords, \
	"flag_isdefault")

    make_array(type_keywords, \
	"string boolean select multiselect note text password")

    make_array(priority_keywords, \
	"low medium high critical")

    make_array(template_keywords, \
	"choices default description template type " \
	"extended_choices extended_default extended_description " \
	"extended_template extended_type")

    make_array(question_keywords, \
	"choices default description template type " \
	"extended_choices extended_default extended_description " \
	"extended_template extended_type " \
	"flag_isdefault name owners value variables")

    nanoconf["templates_db"] = or(templates_db, env("NANOCONF_TEMPLATES"), \
	"/var/lib/nanoconf/templates.db")
    nanoconf["config_db"] = or(config_db, env("NANOCONF_DB"), \
	"/var/lib/nanoconf/debconf.db")
    nanoconf["title"] = or(title, env("NANOCONF_TITLE"),
	"Package Configuration")
    nanoconf["backtitle"] = or(backtitle, env("NANOCONF_BACKTITLE"))
    nanoconf["package"]   = or(package, env("NANOCONF_PACKAGE"))
    nanoconf["auto_go"]   = or(auto_go, env("NANOCONF_AUTO_GO")) # debug

    # Load the database
    db_load(TDB, nanoconf["templates_db"])
    db_load(QDB, nanoconf["config_db"])

    nanoconf["priority"] = tolower(or(priority, env("NANOCONF_PRIORITY"), \
	get_value("debconf/priority"), "normal"))
    nanoconf["frontend"] = tolower(or(frontend, env("NANOCONF_FRONTEND"), \
	get_value("debconf/frontend"), "dialog"))
    nanoconf["showold"] = or(showold, env("NANOCONF_SHOWOLD"), \
	get_value("debconf/showold"), "false")
    nanoconf["lang"] = or(lang, env("LANG"),
	get_value("debconf/lang"))

    if (DEBUG) { parray(nanoconf,"nanoconf","/dev/stderr") }	# debug
}

END {
    run("rm -f "quote(TEMPFILE))
    debug(1,"# nanoconf ended")
}

{ debug(2,"%s %s",NR,$0) }

# Nanoconf command extensions
(tolower($1) == "list")	   { cmd_list($2); next }
(tolower($1) == "load")	   { cmd_load($2,$3); next }
(tolower($1) == "save")	   { cmd_save($2,$3); next }
(tolower($1) == "setvar")  { cmd_set($2,$3,0); next }
(tolower($1) == "package") { cmd_package($1); next }
(tolower($1) == "quit")	   { cmd_quit(); next }
(tolower($1) == "q")	   { exit }
($1 == "")	  	   { next }

# Debconf standard commands
($1 == "BEGINBLOCK")	   { cmd_beginblock(); next }
($1 == "CAPB")		   { cmd_capb(); next }
($1 == "CLEAR")		   { cmd_clear(); next }
($1 == "ENDBLOCK")	   { cmd_endblock(); next }
($1 == "FGET")		   { cmd_fget($2,$3); next }
($1 == "FSET")		   { cmd_fset($2,$3,$4); next }
($1 == "GET")		   { cmd_get($2); next }
($1 == "GO")		   { cmd_go(); next }
($1 == "INPUT")		   { cmd_input($2,$3); next }
($1 == "METAGET")	   { cmd_metaget($2,$3); next }
($1 == "PREVIOUS_MODULE")  { cmd_previous_module(); next }
($1 == "PURGE")		   { cmd_purge(); next }
($1 == "REGISTER")	   { cmd_register($2,$3); next }
($1 == "RESET")		   { cmd_reset($2); next }
($1 == "SET")		   { cmd_set($2,$3,$4); next }
($1 == "STOP")		   { cmd_stop(); next }
($1 == "SUBST")		   { cmd_subst($2,$3,$4); next }
($1 == "TITLE")		   { cmd_title(); next }
($1 == "UNREGISTER")	   { cmd_unregister($2); next }
($1 == "VERSION")	   { cmd_version($2); next }

# Debconf command aliases
($1 == "NOTE")		   { cmd_input($2,$3); next }
($1 == "TEXT")		   { cmd_input($2,$3); next }

# Unknown input
{ cmd_unknown() }

function cmd_beginblock() {
    debug(1,"# cmd_beginblock")
    print 0
}

# Capabilities are in $2-$N
function cmd_capb(x) {
    $1 = ""; sub("^ \t+","")
    debug(1,"# cmd_capb %s",$0)
    print 0, "multiselect backup"
}

function cmd_clear() {
    debug(1,"# cmd_clear")
    delete_all_questions()
    print 0
}

function cmd_endblock() {
    debug(1,"# cmd_endblock")
    print 0
}

function cmd_fget(var,flag) {
    debug(1,"# cmd_fget %s %s",var,flag)
    if (undefined(var)) return
    print 0, db_get_field(QDB,var,"flag_"flag)
}

function cmd_fset(var,flag,val) {
    debug(1,"# cmd_fset %s %s %s",var,flag,val)
    if (undefined(var)) return
    print 0, db_set_field(QDB,var,"flag_"flag,val)
}

function cmd_get(var,  val,isdefault) {
    debug(1,"# cmd_get %s",var)
    if (undefined(var)) return
    print 0, get_value(var)
}

function cmd_go() {
    debug(1,"# cmd_go")
    if (ask_all_questions()) {
	print 0
    } else {
	# Backup request from user
	print 30
    }
    delete_all_questions()
}

function cmd_input(priority,var) {
    debug(1,"# cmd_input %s %s",priority,var)
    if (undefined(var)) return
    if (!add_question(var,priority)) {
	# Old question or not allowed by current priority 
	print 30
	return
    }
    if (nanoconf["auto_go"]) { return cmd_go() }    # debug
    print 0
}

function cmd_list(expr,  nkeys,_K,_T,d) {
    debug(1,"# cmd_list %s",expr)
    gsub("\\*",".*",expr)
    split(QDB[""],_T)
    for (i in _T) {
	if (expr && (!match(_T[i],"^"expr))) continue
	_K[++nkeys] = _T[i]
    }
    delete _T
    isort(_K, nkeys)
    for (i=1; i<=nkeys; i++) {
	d=(is_default(_K[i]) ? "default" : "")
	print _K[i]"='"get_value(_K[i])"' [" d"]" > STDERR
    }
    delete _K
    print 0
}

function cmd_load(which,file) {
    debug(1,"# cmd_load %s %s",which,file)
    if (match("templates","^"which)) {
	print 30*(!db_load(TDB, file))
	return
    }
    if ((match("questions","^"which)) || (match("db","^"which))) {
	print 30*(!db_load(QDB, file))
	return
    }
    if (match("file","^"which)) {
	print 30*(!db_load_template(file))
	return
    }
    print 20, "syntax error"
}

function cmd_metaget(var,field,  val) {
    debug(1,"# cmd_metaget %s %s",var,field)
    if (undefined(var)) return
    val = db_get_field(QDB,var,field)
    if (db_undef(QDB)) {
	val = db_get_field(TDB,var,field)
    }
    print 0, val
}

function cmd_package(p) {
    debug(1,"# cmd_package %s",p)
    if (p) {
	nanoconf["package"] = p
    }
    print 0, nanoconf["package"]
}

function cmd_previous_module() {
    debug(1,"# cmd_previous_module")
    print 30, "not supported"
}

# XXX - todo
function cmd_purge() {
    debug(1,"# cmd_purge")
    # delete all vars and templates for current package
    print 0
}

# Exit without saving
function cmd_quit() {
    debug(1,"# cmd_quit")
    print 0, "quit"
    exit 0
}

# XXX - todo
function cmd_register(template,var) {
    debug(1,"# cmd_register %s %s",template,var)
    print 0
}

function cmd_reset(var) {
    debug(1,"# cmd_reset %s",var)
    if (undefined(var)) return
    set_value(var, get_default(var), 1)
    print 0
}

function cmd_save(which,file) {
    debug(1,"# cmd_save %s %s",which,file)
    if (!which) {
	print 30*(!db_save(TDB, nanoconf["templates_db"]) || \
		 (!db_save(QDB, nanoconf["config_db"])))
	return
    }
    if (match("templates","^"which)) {
	print 30*(!db_save(TDB, file))
	return
    }
    if ((match("questions","^"which)) || (match("db","^"which))) {
	print 30*(!db_save(QDB, file))
	return
    }
    print 20, "syntax error"
}

# Optional arg isdefault is a nanoconf extension
function cmd_set(var,val,isdefault) {
    debug(1,"# cmd_set %s %s %s",var,val,isdefault)
    if (undefined(var)) return
    set_value(var,val,isdefault)
    print 0
}

# cmd_stop exits without printing anything
function cmd_stop() {
    debug(1,"# cmd_stop")
    db_save(TDB, nanoconf["templates_db"])
    db_save(QDB, nanoconf["config_db"])
    exit 0
}

# XXX - todo
function cmd_subst(var,key,val) {
    debug(1,"# cmd_subst %s %s %s",var,key,val)
    print 30, "not supported"
}

# Title string is in $2-$N
function cmd_title() {
    $1 = ""; sub("^ \t+","")
    debug(1,"# cmd_title %s",$0)
    nanoconf["title"] = $0
    print 0
}

function cmd_unknown(var) {
    debug(1,"# cmd_unknown %s",$1)
    print 20, "Unsupported command "$1" received from confmodule."
}

# XXX - todo
function cmd_unregister(var) {
    debug(1,"# cmd_unregister %s",var)
    print 0
}

function cmd_version(vers,  p,v) {
    debug(1,"# cmd_version %s",vers)
    v = vers; sub("\..*","",v)
    p = PROTOCOL_VERSION; sub("\..*","",p)
    if (v < p) {
	print 30, "Version too low (" vers ")"
	return
    }
    if (v > p) {
	print 30, "Version too high (" vers ")"
	return
    }
    print 0, PROTOCOL_VERSION
}

# end of file
# Misc utilities
#
#   max(x,y)
#   min(x,y)
#   or(...)
#   env(x)
#   getpid()
#   date(fmt)
#   run(cmd)
#   readfile(file)
#   readpipe(cmd)
#   test_x(file)
#   escape(text,chars)
#   quote(text,q)
#   wrap(text,width)
#   debug(level,fmt,...)
#   make_array(table,flags)
#   parray(array,aname,file,expr)
#   isort(A,n)

function max(x,y) {
    return ((x>y)?x:y)
}

function min(x,y) {
    return ((x<y)?x:y)
}

function or(a,b,c,d,e) {
    if (a) return a
    if (b) return b
    if (c) return c
    if (d) return d
    if (e) return e
}

function env(x) {
    return ENVIRON[x]
}

function getpid( cmd,x) {
    cmd = "echo $PPID"
    cmd | getline x
    close(cmd)
    return x
}

function date(fmt,  cmd,x) {
    if (fmt) fmt = quote(fmt)
    cmd = "date "fmt
    cmd | getline x
    close(cmd)
    return x
}

function run(cmd,  rc) {
    if (DEBUG) { print "# run "cmd > STDERR }	# debug printf limit is 1020
    rc = system(cmd)
    debug(2,"# rc="rc)
    return rc
}

function readfile(file,  result) {
    debug(2, "# readfile %s", file)
    while ((getline line < file) > 0) {
	result = result"\n"line
    }
    close(file)
    sub("\n","",result)
    return result
}

function readpipe(cmd,  result) {
    debug(2, "# readpipe %s", cmd)
    while ((cmd | getline line) > 0) {
	result = result"\n"line
    }
    close(cmd)
    sub("\n","",result)
    return result
}

function test_x(file) {
    return !system("test -x " escape(file))
}

function escape(text,chars,  i,c) {
    gsub("\\\\","\\\\\\\\",text)
    chars = or(chars,"'")
    for (i=1; i<=length(chars); i++) {
	c = substr(chars,i,1); gsub("\\"c,"\\\\"c,text)
    }
    return text
}

function quote(text,q) {
    q = or(q,"\"")
    return q escape(text,q"`$") q
}

function wrap(text,width,  _wrap,n,i,j,k,line,s,c) {
    n = split(text,_wrap,"\n"); text = ""; width=or(width,72)
    for (i=1; i<=n; i++) {
	line = _wrap[i]
	while ((l=length(line)+7*gsub("\t","\t",line)) > width) {
	    k = width
	    for (j=1; j<=(width+1); j++) {
		c = substr(line,j,1)
		if ((c==" ")||(c=="\t")) { k = j }
	    }
	    text = text"\n"substr(line,1,k)
	    line = substr(line,k+1); sub("^[ \t]+","",line)
	}
	text = text"\n"line
    }
    delete _wrap
    sub("\n","",text)
    return text
}

# Debug is required also in non-debug mode
function debug(level,fmt,a,b,c,d,e,f,g,h,i,j,k,l,m,n) {
    if (level+0 == 0) {
	# Unconditional debug, level is the fmt string
	printf (level"\n",fmt,a,b,c,d,e,f,g,h,i,j,k,l,m,n) > STDERR
    } else if (level+0 <= DEBUG) {
	printf (fmt"\n",a,b,c,d,e,f,g,h,i,j,k,l,m,n) > STDERR
    }
    fflush(STDERR)
}

function make_array(table,flags,  i,n,_array) {
    n = split(flags,_array)
    for (i=1; i<=n; i++) {
	table[_array[i]] = _array[i]
    }
    delete _array
}

function parray(array,aname,file,expr,  _parray,nkeys) {
    if (!aname) { aname = "array" }
    if (!file) { file = "/dev/stdout" }
    for (key in array) {
	if (expr && (!match(key,expr))) continue
	_parray[++nkeys] = key
    }
    isort(_parray, nkeys)
    print aname "=" nkeys >> file
    for (i=1; i<=nkeys; i++) {
	key = _parray[i]
	print aname"["key"]='"array[key]"'" >> file
    }
    delete _parray
    print "" >> file
}

function isort(A,n,  i,j,t) {
    for(i=2; i<=n; i++) {
	t=A[j=i]
	while (A[j-1]>t) { j--; A[j+1]=A[j] }
	A[j]=t
    }
}

# end of file
# Function for accessing debconf variables
#
#   undefined(var)
#   is_default(var)
#   get_value(var)
#   set_value(var,val,isdefault)
#   get_field(var,field)
#   set_field(var,field,val)
#   get_type(var)
#   get_default(var)
#   get_prompt(var)
#   get_text(var)
#   get_l10n_field(var,field,lang)

# Prints an error message if a variable is not defined
function undefined(var) {
    if (!db_exists(QDB,var)) {
	print 10, var, "doesn't exist"
	return 1
    }
    return 0
}

function is_default(var) {
    if (!db_exists(QDB,var)) {
	return 1
    }
    return (db_get_field(QDB,var,"flag_isdefault") == "true")
}

# Returns var value or template default value
function get_value(var,  val) {
    # if (!db_exists(QDB,var)) {
    #	return
    # }
    val = db_get_value(QDB,var)
    if (db_undef(QDB)) {
	val = db_get_field(TDB,var,"default")
    }
    return val
}

function set_value(var,val,isdefault) {
    if (!db_exists(QDB,var)) {
	return
    }
    db_set_value(QDB,var,val,isdefault)
    return val
}

# Returns a field from questions or templates db
function get_field(var,field,  val) {
    # if (!db_exists(QDB,var)) {
    #	return
    # }
    val = db_get_field(QDB,var,field)
    if (db_undef(QDB)) {
	val = db_get_field(TDB,var,field)
    }
    return val
}

function set_field(var,field,val) {
    if (!db_exists(QDB,var)) {
	return
    }
    if ((field in flag_keywords) || (field in question_keywords)) {
	debug(1,"# set_field QDB %s %s = %s",var,field,val)
	val = db_set_field(QDB,var,field,val)
    } else {
	debug(1,"# set_field TDB %s %s = %s",var,field,val)
	val = db_set_field(TDB,var,field,val)
    }
    return val
}

function get_type(var) {
    # if (!db_exists(QDB,var)) {
    #	return
    # }
    return db_get_field(TDB,var,"type")
}

function get_default(var) {
    # if (!db_exists(QDB,var)) {
    #	return
    # }
    return db_get_field(TDB,var,"default")
}

function get_prompt(var,  prompt) {
    prompt = get_l10n_field(var, "description", nanoconf["lang"])
    return or(prompt, "Enter value of "var)
}

function get_text(var) {
    return get_l10n_field(var, "extended_description", nanoconf["lang"])
}

# Get a localized template field. Try langs in this order: ll_LL, ll, ""
function get_l10n_field(var,field,lang,  ret) {
    if (lang) {
	ret = db_get_field(TDB, var, field"-"lang)
	if (!ret && sub("_..$","",lang)) {
	    ret = db_get_field(TDB, var, field"-"lang)
	}
    }
    if (!ret) {
	ret = db_get_field(TDB, var, field)
    }
    return ret
}

# end of file
# Nanoconf database
#
#   db_load(db,file)
#   db_load_aux(db,file,name,indent)
#   db_save(db,file)
#   db_save_aux(db,file,name,last,indent)
#   db_load_template(file)
#   db_add(db,keywords,name,this)
#   db_delete(db,keywords,name,this)
#   db_set_field(db,name,key,val,type)
#   db_get_field(db,name,key)
#   db_get_value(db,name,default)
#   db_set_value(db,name,val,isdefault)
#   db_exists(db,name)
#   db_undef(db)
#
# database internal format:
#
#   db[""]		<list of names>
#   db[:name]		perl hash name
#   db[name:type]	number, list, ref, text (default)
#   db[name:class]	<classname>
#   db[name]		<value> if type != list
#   db[name]		<list of subnames> if type == list
#   db[name=>subname]	subfield notation

# Load a debconf database
function db_load(db,file,  rc) {
    debug(1,"# db_load %s",file)
    if (file == "") {
	return 0
    }
    if ((getline < file) <= 0) {
	print "unable to open db: "file > STDERR
	return 0
    }
    if (!match($0,"%[a-z]+ \= \(")) {
	print "invalid db format: "$0 > STDERR
	return 0
    }
    db[":name"] = $1
    $2="=>"; $3="{"
    rc = db_load_aux(db,file)
    debug(2,"# db_load rc=%s",rc)
    close(file)
    if (DEBUG >= 5) {
	parray(db,"db",STDERR)
    }
    return rc
}

function db_load_aux(db,file,name,indent,  subname,subvars,class,val) {
    debug(3,"# db_load_aux name=%s indent='%s  '",name,indent)
    debug(6,"%d>%s",length(indent)/2,$0)
    if ($2 != "=>") {
	print "invalid db format: "$0 > STDERR
	return 0
    }
    sub("^.*\ => ","")
    if (match($1,"^'")) {
	# Text value
	# db[name":type"] = "string" assumed by default
	debug(6,"# text: %s", $0)
	val = $0
	while (!match(val, "[^\\\]',?$")) {
	    if ((getline < file) <= 0) {
		print "eof on string input: "$0 > STDERR
		return 0
	    }
	    debug(7,"+>%s", $0)
	    val = val"\n"$0
	}
	sub("^'","",val); sub("',?$","",val)
	gsub("\\\'","'",val); gsub("\\\\\\\\","\\\\",val);
	db[name] = val
	debug(6,"= db["name"]='%s'", db[name])
	return 1
    }
    sub(",$","")
    if (match($1,"^[0-9]+$")) {
	# Numeric value
	debug(6,"# number: %s", $0)
	db[name":type"] = "number"
	db[name] = $1
	debug(6,"= db["name"]='%s'", db[name])
	return 1
    }
    if (match($1,"^\\$.*{'.*'}$")) {
	# Subst value
	debug(6,"# subst %s", $0)
	db[name":type"] = "subst"
	db[name] = $1
	debug(6,"= db["name"]='%s'", db[name])
	return 1
    }
    if ($1 == "{}") {
	# Empty list
	debug(6,"# empty list: %s", $0)
	db[name":type"] = "list"
	db[name] = ""
	debug(6,"= db["name"]='%s'", db[name])
	return 1
    }
    if ($1 == "{") {
	# Non-empty list
	debug(6,"# list: %s", $0)
	db[name":type"] = "list"
	while ((getline < file) > 0) {
	    debug(6,">>%s", $0)
	    if (match($0,"^"indent"}")) {
		debug(6,"# end list: %s", $0)
		break
	    }
	    if (match($0,"^"indent"  '[a-z][a-z0-9_/.-]+' \=> ")) {
		sub("^"indent"  '",""); sub("' => "," => ")
		class = sub(" => bless\( "," => ")
		debug(7,"++%s", $0)
		if (name == "") {
		    subname = $1
		} else {
		    subname = name"=>"$1
		}
		subvars = subvars" "subname
		if (!db_load_aux(db,file,subname,indent"  ")) {
		    return 0
		}
		if (class) {
		    class = $2; gsub("'","",class)
		    debug(6,"# class: %s", class)
		    db[subname":class"] = class
		    debug(6,"= db["subname":class]='%s'", db[subname":class"])
		}
	    }
	}
	sub("^ ","",subvars)
	db[name] = subvars
	return 1
    }
    print "unknown input: "$0 > STDERR
    return 0
}

# Save a database in debconf format
function db_save(db,file,  i,keys,nkeys) {
    debug(1,"# db_save %s", file)
    if (file == "") {
	return 0
    }
    if (!db[":name"]) {
	printf "invalid db" > STDERR
	return 0
    }
    print db[":name"]" \= \(" > file
    nkeys = split(db[""],keys)
    for (i=1; i<=nkeys; i++) {
	if (!db_save_aux(db,file,keys[i],(i>=nkeys))) {
	    close(file)
	    return 0
	}
    }
    print ");" > file
    print "" > file
    print "1;" > file
    close(file);
    return 1
}

function db_save_aux(db,file,name,last,indent,  i,val,shortname,class,type) {
    debug(3,"# db_save_aux name=%s last=%s indent='%s  '",name,last,indent)
    shortname = name; gsub(".*=>","",shortname)
    last = (last ? "" : ",")
    val = db[name]
    if (name":class" in db) {
	class = db[name":class"]
    }
    if (name":type" in db) {
	type = db[name":type"]
    }
    printf("%s  '%s' => ",indent,shortname) > file
    if (class) {
	printf("bless\( ") > file
    }
    if ((type == "string") || (type == "")) {
	printf("%s", quote(val,"'")) > file
    }
    if (type == "number") {
	printf("%s", val) > file
    }
    if (type == "subst") {
	printf("%s", val) > file
    }
    if ((type == "list") && (!val)) {
	printf("{}") > file
    }
    if ((type == "list") && (val)) {
	print "{" > file
	$0 = db[name]
	for (i=1; i<=NF; i++) {
	    if (!db_save_aux(db,file,$i,(i>=NF), indent"  ")) {
		return 0
	    }
	}
	printf("%s  \}", indent) > file
    }
    if (class) {
	printf(",\ '%s' \)", class) > file
    }
    print last > file
    return 1
}

# Load a template file
function db_load_template(file,  this,name,tag,val,desc) {
    debug(1,"# db_load_template %s", file)
    while (!(eof=(getline < file) <= 0)) {
	debug(6,"%s",$0)
	if (match(tag,"^description") && match($0,"^ ")) {
	    sub("^","extended_",tag)
	    val = $0
	    sub("^ ","",val)
	    while (!(eof=((getline <file) <= 0)) && (match($0,"^ "))) {
		sub("^ \\.$","\n"); sub("^ ","\n");
		val = val $0
	    }
	    this[tag] = val
	    debug(6,"tag=%s, val=%s",tag,val)
	}
	if (eof || !NF) {
	    db_add(QDB, question_keywords, name, this)
	    db_add(TDB, template_keywords, name, this)
	    delete this
	    continue
	}
	if (match($0,"^[A-Z]")) { $1 = tolower($1) }
        tag = $1; sub(":","",tag)
	val = $0; sub(tag": ","",val); sub("^'","",val); sub("'$","",val)
	# if (!template_keywords[tag]) {
	#   print "invalid keyword: "$0 > STDERR; break
	# }
	if (tag == "template") {
	    delete this
	    tag = "name"
	    name = val
	}
        debug(6,"this[%s]=%s",tag,val)
	this[tag] = val
    }
    close(file)
    return 1
}

function db_add(db,keywords,name,this,  key,subvars) {
    if (!name) return
    for (key in this) {
	subvars = subvars" "key
	db_set_field(db, name, key, this[key])
	debug(6,"db[%s]='%s'",name"=>"key,db[name"=>"key])
    }
    for (key in keywords) {
	if ((key != "value") && (!db[name"=>"key])) {
	    db_set_field(db, name, key, "")
	}
    }
    if ("flag_isdefault" in keywords) {
	if (!db[name"=>flag_isdefault"]) {
	    db_set_field(db, name, "flag_isdefault", "true")
	}
	if (!db[name"=>template"]) {
	    db_set_field(db, name, "template", "$templates{'"name"'}")
	    db[name"=>template:type"] = "subst"
	}
	db[name":class"] = "Debian::DebConf::Question"
    } else {
	db[name":class"] = "Debian::DebConf::Template"
    }
    sub("^ ","",subvars)
    db[name] = subvars
    db[""] = db[""]" "name
}

function db_delete(db,keywords,name,this,  key) {
    for (key in db) {
	if (match(key,"^"name"(=>|:)")) {
	    delete db[key]
	}
    }
    if (name in db) {
	delete db[name]
    }
}

function db_set_field(db,name,key,val,type) {
    if (!(name"=>"key in db)) {
	db[name] = db[name]" "key
	sub("^ ","",db[name])
    }
    db[name"=>"key] = val
    if (type) {
	db[name"=>"key":type"] = type
    }
    return val
}

function db_get_field(db,name,key) {
    if (name"=>"key in db) {
	db[":undef"] = 0
	return db[name"=>"key]
    } else {
	db[":undef"] = 1
	return ""
    }
}

function db_get_value(db,name,default,  val) {
    if (!db_exists(db,name)) {
	return(default)
    }
    val = db_get_field(db,name,"value")
    if (db[":undef"]) {
	return default
    } else {
	return val
    }
}

function db_set_value(db,name,val,isdefault) {
    if (!db_exists(db,name)) {
	return
    }
    db_set_field(db,name,"value",val)
    if (isdefault != "") {
	db_set_field(db,name,"flag_isdefault",(isdefault ? "true" : "false"))
    }
    return val
}

function db_exists(db,name) {
    if (name && (db_get_field(db,name,"name") == name)) {
	db[":undef"] = 0
	return 1
    } else {
	db[":undef"] = 1
	return 0
    }
}

# Returns 1 if the last db_get_field was unsuccessful 
function db_undef(db) {
    return (db[":undef"] == 1)
}

# end of file
# Question functions
#
#   add_question(var,priority)
#   delete_all_questions()
#   ask_all_questions()
#   nanoconf_priority(priority)

function add_question(var,p) {
    debug(1,"# add_question %s %s",var,p)
    # Noninteractive mode, questions not allowed except note and text
    if (nanoconf["frontend"] == "noninteractive") {
	if (!match(get_type(var),"(note|text)")) {
	    debug(2,"# noninteractive mode, ignored")
	    return 0
	}
    }
    # Question priority less than debconf priority, question ignored
    if (nanoconf_priority(p) < nanoconf_priority()) {
	debug(2,"# low priority, ignored")
	return 0
    }
    # If showold is not set don't show old questions
    if ((nanoconf["showold"] != "true") && (!is_default(var))) {
	debug(2,"# old question, ignored")
	return 0
    }
    if (!active[var]) {
	debug(2,"# added question %s %s %s",var,get_type(var),p)
	nanoconf["active"] = nanoconf["active"]" "var
	active[var] = p
    }
    return 1
}

function delete_all_questions() {
    debug(1,"# delete_all_questions")
    delete active
    delete nanoconf["active"]
}

function ask_all_questions( var,n,i,_ask_questions) {
    debug(1,"# ask_all_questions")
    rc = 1
    n = split(nanoconf["active"],_ask_questions)
    for (i=1; i<=n; i++) {
	var = _ask_questions[i]
	if (!(rc = show_question(var))) {
	    debug(1,"# user backup: %s",var)
	    break
	}
    }
    delete _ask_questions
    return rc
}

function nanoconf_priority(p) {
    if (!p) {
	p = nanoconf["priority"]
    }
    if (p == "critical") { return 4 }
    if (p == "high")     { return 3 }
    if (p == "medium")   { return 2 }
    if (p == "low")      { return 1 }
    return 0
}

# end of file
# Nanoconf frontend
#
#   show_question(var)
#   show_string(var,type)
#   show_boolean(var,type)
#   show_select(var,type)
#   show_multiselect(var,type)
#   show_note(var,type)
#   show_text(var,type)
#   show_password(var,type)
#   mail_note(text,prompt)
#   run_frontend(type,text,prompt,val,items)
#   frontend_result()
#   frontend_rc()
#
# Frontend interface:
#
#   result = run_frontend(type,text,prompt,val,items)
#
# sets also the nanoconf["result"] and nanoconf["rc"] variables
#
# Items is an array with the following elements:
#
#   items[""]       = tags list
#   items[tag]      = item status (0 or 1), for multiselect
#   items[tag:item] = optional text, normally not used

function show_question(var,  type) {
    type = or(get_type(var), "string")
    debug(1,"# show_question %s %s",var,type)
    if (type == "string")      { rc = show_string(var,type) }
    if (type == "password")    { rc = show_password(var,type) }
    if (type == "boolean")     { rc = show_boolean(var,type) }
    if (type == "select")      { rc = show_select(var,type) }
    if (type == "multiselect") { rc = show_multiselect(var,type) }
    if (type == "note")        { rc = show_note(var,type) }
    if (type == "text")        { rc = show_text(var,type) }
    debug(2,"# rc=%s, %s=%s",rc,var,get_value(var))
    return rc
}

function show_string(var,type,  val) {
    debug(1,"# show_string %s %s",var,type)
    val = run_frontend(type, get_text(var), get_prompt(var), get_value(var))
    if (frontend_rc() == 0) {
	set_value(var,val,0)
	return 1
    } else {
	return 0
    }
}

# Handled by show_string()
function show_password(var,type) {
    debug(1,"# show_password %s %s",var,type)
    return show_string(var,type)
}

function show_boolean(var,type,  val) {
    debug(1,"# show_boolean %s %s",var,type)
    val = run_frontend(type, get_text(var), get_prompt(var), get_value(var))
    if (frontend_rc() >= 0) {
	debug(1,"# booleean=%s", val)
	set_value(var,val,0)
	return 1
    } else {
	return 0
    }    
}

# Handles select and multiselect questions
function show_select(var,type,  choices,val,n,_T) {
    debug(1,"# show_select %s %s",var,type)
    choices = get_field(var,"choices")
    val = get_value(var)
    n = split(choices,_T,",[ \t]*")
    delete _T
    if ((n==0) || ((n==1) && (type!="multiselect"))) {
	debug(2,"# autoselect %s %s",n,choices)
	set_value(var, choices, 0)
	return 1
    }
    val = run_frontend(type, get_text(var), get_prompt(var), val, choices)
    if (frontend_rc() != 0) {
	return 0
    }
    debug(2,"# select=%s",val)
    set_value(var, val, 0)
    return 1
}

# Handled by show_select()
function show_multiselect(var,type) {
    debug(1,"# show_multiselect %s %s",var,type)
    return show_select(var,type)
}

function show_note(var,type,  t) {
    debug(1,"# show_note %s %s",var,type)
    text = get_text(var)
    prompt = get_prompt(var)
    mail_note(text,prompt)
    run_frontend(type,text,prompt)
    if (frontend_rc() == 0) {
	set_field(var,"flag_isdefault",0)
    }
    return 1
}

function show_text(var,type) {
    debug(1,"# show_text %s %s",var,type)
    run_frontend(type, get_text(var), get_prompt(var))
    set_field(var,"flag_isdefault",0)
    return 1
}

function mail_note(text,prompt) {
    print "From nanoconf  "date("+%a %b %e %T %Y") >> MBOX
    print "Date: "date() >> MBOX
    print "From: nanoconf <root at localhost>" >> MBOX
    print "Subject: "quote(nanoconf["package"]) >> MBOX
    print "" >> MBOX
    print prompt >> MBOX
    print text >> MBOX
    print "" >> MBOX
    close(MBOX)
}

function run_frontend(type,text,prompt,val,items) {
    if (nanoconf["frontend"] == "dialog") {
	return run_dialog_frontend(type,text,prompt,val,items)
    } else {
	return run_text_frontend(type,text,prompt,val,items)
    }
}

# Returns result of last frontend run
function frontend_result() {
    return nanoconf["result"]
}

# Returns rc of last frontend run (0=success)
function frontend_rc() {
    return nanoconf["rc"]
}

# end of file
# Dialog frontend for nanoconf
#
# Frontend interface:
#
#   result = run_frontend(type,text,prompt,val,items)
#   rc = frontend_rc()
#
# sets also the nanoconf["result"] and nanoconf["rc"] variables
#
# Items is an array with the following elements:
#
#   items[""]       = tags list
#   items[tag:item] = optional text, normally not used
#   items[tag]      = item status, 0 or 1
#
# whiptail usage:
#
#   whiptail --yesno       text height width
#   whiptail --msgbox      text height width
#   whiptail --infobox     text height width
#   whiptail --inputbox    text height width [init]
#   whiptail --passwordbox text height width [init]
#   whiptail --textbox     file height width
#   whiptail --menu        text height width menuheight [tag item]...
#   whiptail --checklist   text height width listheight [tag item status]...
#   whiptail --radiolist   text height width listheight [tag item status]...
#   whiptail --gauge       text height width percent
#
# with:
#
#   dialog  = stdout
#   result  = stderr
#   retcode = $?
#
# Example:
#
#   val="$(whiptail --inputbox text 10 40 default 2>&1 </dev/tty >/dev/tty)"
#   rc=$?

BEGIN {
    setup_dialog_frontend()
}

function setup_dialog_frontend() {
    if (test_x("/usr/bin/whiptail")) {
	# Whiptail parameters
	DLG["program"] = "whiptail"
	DLG["bd"] = 5
	DLG["bh"] = 6
	DLG["sp"] = 1
	DLG["ts"] = 10
	DLG["cs"] = 3

	# Mapping between debconf types and dialog types
	DLG["string"]      = "--inputbox"
	DLG["password"]    = "--passwordbox"
	DLG["boolean"]     = "--yesno"
	DLG["select"]      = "--menu"
	DLG["multiselect"] = "--checklist"
	DLG["note"]        = "--msgbox"
	DLG["text"]        = "--infobox"
    } else {
	print "unable to find whiptail" > STDERR
	exit 1
    }
}

function run_dialog_frontend(type,text,prompt,val,choices, \
			     tw,th,w,h,m,n,i,s,rc,tag,_T,_V) {
    debug(2,"# run_dialog_frontend %s, %s [%s]",type,prompt,val)

    # Translate debconf var type to whiptail box type
    type = or(DLG[type],"--inputbox")
    if ((type == "note") && (nanoconf["frontend"] == "noninteractive")) {
	type = "--infobox"
    }

    # Wrap text and compute text width
    if (text && prompt) {
	text = text"\n\n"prompt
    } else {
	text = prompt
    }
    tw = or(env("COLUMNS"),80)-DLG["bd"]-DLG["cs"]
    th = or(env("ROWS"),25)-DLG["bh"]-2
    text = wrap(text,tw)
    h = split(text,_T,"\n")
    for (w=i=1; i<=h; i++) { w = max(w,length(_T[i])) }; delete _T
    # for dbugging very long text
    # text = text"\n"text"\n"text"\n"text"\n"text"\n"text"\n"text"\n"text

    # Compute menu item count
    if (match(type,"(menu|checklist|radiolist)")) {
	n = split(val,_T,",[ \t]*")
        for (i=1; i<=n; i++) { _V[_T[i]] = 1 }
	n = split(choices,_T,",[ \t]*")
    }

    # Compute dialog geometry
    w = max(w,40)
    h = max(h,1)
    s = ((h>th) ? "--scrolltext" : "")
    w = w+DLG["bd"]
    h = min(h+n,th)
    m = min(max(th-h-1,3),n)
    h = h+DLG["bh"]
    text = quote(text)
    debug(3,"# tw=%s, th=%s, w=%s, h=%s, s=%s", tw,th,w,h,s) 
    if (DEBUG) { print "# text='"text"'" > STDERR } # debug printf limit is 1020

    # Build the base command
    cmd = sprintf("%s --backtitle %s --title %s %s %s", DLG["program"], \
		  quote(nanoconf["backtitle"]), quote(nanoconf["title"]),
		  s, type)
    debug(5,"# cmd=%s.",cmd)

    # Add val option for checklists
    if (match(type,"checklist")) { cmd = cmd" --separate-output" }

    # Add text and base geometry
    cmd = cmd" "text" "h" "w

    # Add val argument where allowed
    if (match(type,"(yesno|inputbox|gauge)")) {
	if (match(type,"yesno")) {
	    if (val != "true") { val = "--defaultno" }
	}
	if (match(type,"gauge")) {
	    val = max(min(val,100),0)
	}
	if (val) { cmd = cmd" "quote(val) }
    }

    # Add menu height and items. Note that m could be less than n
    if (match(type,"(menu|checklist|radiolist)")) {
	cmd = cmd" "m
	# Put default menu value first
	if (match(type,"menu") && (val!="")) {
	     cmd = cmd" "quote(val)" ''"
	}
	for (i=1; i<=n; i++) {
	    tag = _T[i];
	    if (match(type,"menu") && (tag==val)) continue
	    s = ((tag in _V) ? "on" : "off")
	    cmd = cmd" "quote(tag)" ''"
	    if (!match(type,"menu")) {
		cmd = cmd" "s
	    }
	}
    }

    # Redirect dialog input and output
    cmd = cmd" <"TTY" >"TTY" 2>"TEMPFILE

    # Run the dialog command
    printf "" > TEMPFILE
    result = ""
    rc = run(cmd)

    # Interpret the result
    # if (match(type,"(msgbox|textbox|gauge")) {
    # }
    if (match(type,"infobox")) {
	system("sleep 1")
	rc = 1
    }
    if (match(type,"yesno") && (rc >= 0)) {
	result = ((rc == 0) ? "true" : "false")
    }
    if (match(type,"(inputbox|passwordbox|menu|checklist|radiolist)")) {
	if (rc == 0) {
	    result = readfile(TEMPFILE)
	    if (match(type,"checklist")) {
		m = split(result, _T, "\n")
		result = ""
		for (i=1; i<=m; i++) {
		    result = result", "_T[i]
		}
		sub(", ","",result)
		delete _T
	    }
	}
    }

    debug(2,"# rc=%s, result=%s",rc,result)
    nanoconf["result"] = result
    nanoconf["rc"] = rc
    return result
}

# end of file
# Text frontend for nanoconf
#
# Frontend interface:
#
#   result = run_frontend(type,text,prompt,val,items)
#   rc = frontend_rc()
#
# sets also the nanoconf["result"] and nanoconf["rc"] variables
#
# Items is an array with the following elements:
#
#   items[""]    = <item tag list>
#   items[tag]   = <item text>
#   items[s:tag] = <item status>

function run_text_frontend(type,text,prompt,val,choices, \
			   tw,m,n,i,s,tag,x,rc,_T,_V) {
    debug(2,"# run_text_frontend %s %s",type,prompt)

    # Display title only for the first time
    if (nanoconf["title"] && TXT["title"] != nanoconf["title"]) {
	TXT["title"] = nanoconf["title"]
	print "" > TTY
	print nanoconf["title"] > TTY
	l = length(TXT["title"])
	for (i=1; i<=l; i++) { printf("=") > TTY }
	print "" > TTY
    }
    print "" > TTY

    # Shortcut for note and text questions
    if (match(type,"(note|text)")) {
	print prompt > TTY
	print text > TTY
	print "" > TTY
	if ((type == "note") && (nanoconf["frontend"] != "noninteractive")) {
	    printf("Press ENTER...") > TTY; getline x < TTY
	    nanoconf["rc"] = 0
	    return 0
	} else {
	    nanoconf["rc"] = 1
	    return 1
	}
    }

    # Print the extended description first
    tw = or(env("COLUMNS"),80)
    print wrap(text,tw) "\n" > TTY

    # Print available choices for select and multiselect questions
    if (match(type,"(select|multiselect)")) {
	n = split(val,_T,",[ \t]*")
        for (i=1; i<=n; i++) { _V[_T[i]] = 1 }
	n = split(choices,_T,",[ \t]*")
	m = 1
	# Print default menu value first
	if ((type=="select") && (val!="")) {
	    printf("%4s  %s\n",0,val) > TTY
	    _T[0] = val
	    m = 0
	}
	for (i=1; i<=n; i++) {
	    tag = _T[i]
	    if (type == "multiselect") {
		s = ((tag in _V) ? "[*]" : "   ")
	    }
	    printf("%4s %s %s\n",i,s,tag) > TTY
	}
	if (type == "multiselect") {
	    printf("%4s %s %s\n","n","   ","<none>") > TTY
	}
	print "" > TTY
    }

    # Loop until a valid answer is received
    while (1) {
	result = ""
	rc = 0

	# Print prompt and current value
	if (type == "string") {
	    printf("%s [%s] ", prompt, val) > TTY
	}
	if (type == "password") {
	    printf("%s ", prompt) > TTY
	    run("stty -echo <"TTY)
	}
	if (type == "boolean") {
	    printf("%s [%s] ", prompt, ((val=="true") ? "Y/n" : "N/y")) > TTY
	}
	if (match(type,"(select|multiselect)")) {
	    printf("%s ", prompt) > TTY
	}

	# Read user input
	rc = ((getline answer < TTY) == 1) - 1
	if (type == "password") {
	    run("stty echo <"TTY)
	    print "" > TTY
	}
	if (rc < 0) {
	    break
	}

	if (type == "string") {
	    result = ((answer != "") ? answer : val)
	    break
	}
	if (type == "password") {
	    result = answer
	    if (result != "") {
		break
	    }
	}
	if (type == "boolean") {
	    answer = tolower(answer)
	    if (match(answer,"^y(es)?$")) { result = "true";  break }
	    if (match(answer,"^no?$"))    { result = "false"; break }
	    if ((answer == "") && (val))  { result = val;     break }
	}
	if (type == "select") {
	    if (answer == "") answer = 0
	    if ((answer>=m) && (answer<=n)) { result = _T[answer]; break }
	}
	if (type == "multiselect") {
	    if (answer == "") {
		result = val
		break
	    }
	    if (match(answer,"^[Nn]$")) {
		break
	    }
	    errors = 0
	    x = split(answer,_V,"[, \t]+")
	    for (i=1; (i<=x); i++) {
		v = _V[i]
		if ((v>=m) && (v<=n)) {
		    result = result", "_T[v]
		} else {
		    errors++
		}
	    }
	    sub(", ","",result)
	    if (!errors) break
	}
	# Invalid answer, loop again
    }

    debug(2,"# rc=%s, result=%s",rc,result)
    nanoconf["result"] = result
    nanoconf["rc"] = rc
    return result
}

# end of file
# Debug commands

($1 == "dump")	  	  { cmd_dump($2,$3); next }
($1 == "show")	  	  { cmd_show($2,$3); next }
($1 == "del")	  	  { cmd_del($2,$3); next }

function cmd_dump(which,expr) {
    if (!expr) { expr=which; which="" }
    if (!which || (match("templates","^"which))) {
	parray(TDB,"templates","",expr)
    }
    if (!which || (match("questions","^"which))) {
	parray(QDB,"questions","",expr)
    }
}

function cmd_show(which,expr,  key) {
    if (!expr) { expr=which; which="" }
    if (!which || (match("templates","^"which))) {
	if (key in templates) {
	    parray(TDB,"templates","","^"expr"$")
	}
    }
    if (!which || (match("questions","^"which))) {
	if (key in questions) {
	    parray(QDB,"questions","","^"expr"$")
	}
    }
}

function cmd_del(which,name) {
    if (!expr) { expr=which; which="" }
    if (!which || (match("templates","^"which))) {
	db_delete(TDB,name)
    }
    if (!which || (match("questions","^"which))) {
	db_delete(QDB,name)
    }
}

# end of file


-- 
see shy jo





More information about the Crackmonkey mailing list