18097Sjkh/*
28097Sjkh * Miscellaneous support routines..
38097Sjkh *
450479Speter * $FreeBSD$
58097Sjkh *
68097Sjkh * Copyright (c) 1995
78097Sjkh *	Jordan Hubbard.  All rights reserved.
88097Sjkh *
98097Sjkh * Redistribution and use in source and binary forms, with or without
108097Sjkh * modification, are permitted provided that the following conditions
118097Sjkh * are met:
128097Sjkh * 1. Redistributions of source code must retain the above copyright
138881Srgrimes *    notice, this list of conditions and the following disclaimer,
148881Srgrimes *    verbatim and that no modifications are made prior to this
158097Sjkh *    point in the file.
168097Sjkh * 2. Redistributions in binary form must reproduce the above copyright
178097Sjkh *    notice, this list of conditions and the following disclaimer in the
188097Sjkh *    documentation and/or other materials provided with the distribution.
198097Sjkh *
208097Sjkh * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
218097Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
228097Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
238097Sjkh * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
248097Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
258097Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
268097Sjkh * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
278097Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
288097Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
298097Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
308097Sjkh * SUCH DAMAGE.
318097Sjkh *
328097Sjkh */
338097Sjkh
348097Sjkh#include "sysinstall.h"
358097Sjkh#include <ctype.h>
368709Sjkh#include <unistd.h>
378556Sjkh#include <sys/stat.h>
388556Sjkh#include <sys/errno.h>
398556Sjkh#include <sys/file.h>
408556Sjkh#include <sys/types.h>
4112661Speter#include <dirent.h>
428556Sjkh#include <sys/wait.h>
438556Sjkh#include <sys/param.h>
448556Sjkh#include <sys/mount.h>
4524597Sjkh#include <ufs/ufs/ufsmount.h>
468556Sjkh#include <sys/reboot.h>
478556Sjkh#include <sys/disklabel.h>
48107525Smarcel#include <fs/msdosfs/msdosfsmount.h>
49160380Ssimon#include <sys/sysctl.h>
508097Sjkh
518097Sjkh/* Quick check to see if a file is readable */
528097SjkhBoolean
538097Sjkhfile_readable(char *fname)
548097Sjkh{
558097Sjkh    if (!access(fname, F_OK))
568097Sjkh	return TRUE;
578097Sjkh    return FALSE;
588097Sjkh}
598097Sjkh
608097Sjkh/* Quick check to see if a file is executable */
618097SjkhBoolean
628097Sjkhfile_executable(char *fname)
638097Sjkh{
648097Sjkh    if (!access(fname, X_OK))
658097Sjkh	return TRUE;
668097Sjkh    return FALSE;
678097Sjkh}
688097Sjkh
698097Sjkh/* Concatenate two strings into static storage */
708097Sjkhchar *
718097Sjkhstring_concat(char *one, char *two)
728097Sjkh{
738097Sjkh    static char tmp[FILENAME_MAX];
748097Sjkh
7512661Speter    /* Yes, we're deliberately cavalier about not checking for overflow */
768097Sjkh    strcpy(tmp, one);
778097Sjkh    strcat(tmp, two);
788097Sjkh    return tmp;
798097Sjkh}
808097Sjkh
8120247Sjkh/* sane strncpy() function */
8220247Sjkhchar *
8320247Sjkhsstrncpy(char *dst, const char *src, int size)
8420247Sjkh{
8520569Sjkh    dst[size] = '\0';
8620377Sjkh    return strncpy(dst, src, size);
8720247Sjkh}
8820247Sjkh
8912661Speter/* Concatenate three strings into static storage */
9012661Speterchar *
9112661Speterstring_concat3(char *one, char *two, char *three)
9212661Speter{
9312661Speter    static char tmp[FILENAME_MAX];
9412661Speter
9512661Speter    /* Yes, we're deliberately cavalier about not checking for overflow */
9612661Speter    strcpy(tmp, one);
9712661Speter    strcat(tmp, two);
9812661Speter    strcat(tmp, three);
9912661Speter    return tmp;
10012661Speter}
10112661Speter
1028097Sjkh/* Clip the whitespace off the end of a string */
1038097Sjkhchar *
1048097Sjkhstring_prune(char *str)
1058097Sjkh{
1068097Sjkh    int len = str ? strlen(str) : 0;
1078097Sjkh
1088097Sjkh    while (len && isspace(str[len - 1]))
1098097Sjkh	str[--len] = '\0';
1108097Sjkh    return str;
1118097Sjkh}
1128097Sjkh
1138097Sjkh/* run the whitespace off the front of a string */
1148097Sjkhchar *
1158097Sjkhstring_skipwhite(char *str)
1168097Sjkh{
1178097Sjkh    while (*str && isspace(*str))
1188097Sjkh	++str;
1198097Sjkh    return str;
1208097Sjkh}
1218097Sjkh
12212661Speter/* copy optionally and allow second arg to be null */
12312661Speterchar *
12412661Speterstring_copy(char *s1, char *s2)
12512661Speter{
12612661Speter    if (!s1)
12712661Speter	return NULL;
12812661Speter    if (!s2)
12912661Speter	s1[0] = '\0';
13012661Speter    else
13112661Speter	strcpy(s1, s2);
13212661Speter    return s1;
13312661Speter}
13412661Speter
13516462Sjkh/* convert an integer to a string, using a static buffer */
13616462Sjkhchar *
13716462Sjkhitoa(int value)
13816462Sjkh{
13916462Sjkh    static char buf[13];
14016462Sjkh
14116462Sjkh    snprintf(buf, 12, "%d", value);
14216462Sjkh    return buf;
14316462Sjkh}
14416462Sjkh
14512661SpeterBoolean
14614670Sjkhdirectory_exists(const char *dirname)
14712661Speter{
14812661Speter    DIR *tptr;
14912661Speter
15012661Speter    if (!dirname)
15112661Speter	return FALSE;
15212661Speter    if (!strlen(dirname))
15312661Speter	return FALSE;
15412661Speter
15512661Speter    tptr = opendir(dirname);
15612661Speter    if (!tptr)
15712661Speter	return (FALSE);
15812661Speter
15912661Speter    closedir(tptr);
16012661Speter    return (TRUE);
16112661Speter}
16212661Speter
16312661Speterchar *
16412661SpeterpathBaseName(const char *path)
16512661Speter{
16612661Speter    char *pt;
16712661Speter    char *ret = (char *)path;
16812661Speter
16912661Speter    pt = strrchr(path,(int)'/');
17012661Speter
17112661Speter    if (pt != 0)			/* if there is a slash */
17212661Speter    {
17312661Speter	ret = ++pt;			/* start the file after it */
17412661Speter    }
17512661Speter
17612661Speter    return(ret);
17712661Speter}
17812661Speter
1798097Sjkh/* A free guaranteed to take NULL ptrs */
1808097Sjkhvoid
1818097Sjkhsafe_free(void *ptr)
1828097Sjkh{
1838097Sjkh    if (ptr)
1848097Sjkh	free(ptr);
1858097Sjkh}
1868097Sjkh
1878208Sjkh/* A malloc that checks errors */
1888208Sjkhvoid *
1898208Sjkhsafe_malloc(size_t size)
1908208Sjkh{
1918208Sjkh    void *ptr;
1928208Sjkh
1938363Sjkh    if (size <= 0)
19483842Smurray	msgFatal("Invalid malloc size of %ld!", (long)size);
1958208Sjkh    ptr = malloc(size);
1968208Sjkh    if (!ptr)
1978208Sjkh	msgFatal("Out of memory!");
19812661Speter    bzero(ptr, size);
1998208Sjkh    return ptr;
2008208Sjkh}
2018208Sjkh
2028363Sjkh/* A realloc that checks errors */
2038363Sjkhvoid *
2048363Sjkhsafe_realloc(void *orig, size_t size)
2058363Sjkh{
2068363Sjkh    void *ptr;
2078363Sjkh
2088363Sjkh    if (size <= 0)
20983842Smurray	msgFatal("Invalid realloc size of %ld!", (long)size);
210108804Sobrien    ptr = reallocf(orig, size);
2118363Sjkh    if (!ptr)
2128363Sjkh	msgFatal("Out of memory!");
2138363Sjkh    return ptr;
2148363Sjkh}
2158363Sjkh
21617034Sjkh/* Create a path biased from the VAR_INSTALL_ROOT variable (if not /) */
21717034Sjkhchar *
21817034Sjkhroot_bias(char *path)
21917034Sjkh{
22017034Sjkh    static char tmp[FILENAME_MAX];
22117034Sjkh    char *cp = variable_get(VAR_INSTALL_ROOT);
22217034Sjkh
22317034Sjkh    if (!strcmp(cp, "/"))
22417034Sjkh	return path;
22517034Sjkh    strcpy(tmp, variable_get(VAR_INSTALL_ROOT));
22617034Sjkh    strcat(tmp, path);
22717034Sjkh    return tmp;
22817034Sjkh}
22917034Sjkh
2308097Sjkh/*
23126598Sjkh * These next routines are kind of specialized just for building item lists
2328097Sjkh * for dialog_menu().
2338097Sjkh */
2348174Sjkh
23515242Sjkh/* Add an item to an item list */
23615242SjkhdialogMenuItem *
23715242Sjkhitem_add(dialogMenuItem *list, char *prompt, char *title,
23815242Sjkh	 int (*checked)(dialogMenuItem *self),
23915242Sjkh	 int (*fire)(dialogMenuItem *self),
24015242Sjkh	 void (*selected)(dialogMenuItem *self, int is_selected),
241209359Srandi	 void *data, void *aux, int *curr, int *max)
2428097Sjkh{
24315242Sjkh    dialogMenuItem *d;
2448097Sjkh
2458097Sjkh    if (*curr == *max) {
2468097Sjkh	*max += 20;
247108804Sobrien	list = (dialogMenuItem *)safe_realloc(list, sizeof(dialogMenuItem) * *max);
2488097Sjkh    }
24915242Sjkh    d = &list[(*curr)++];
25015419Sjkh    bzero(d, sizeof(*d));
25115242Sjkh    d->prompt = prompt ? strdup(prompt) : NULL;
25215242Sjkh    d->title = title ? strdup(title) : NULL;
25315242Sjkh    d->checked = checked;
25415242Sjkh    d->fire = fire;
25515242Sjkh    d->selected = selected;
25615242Sjkh    d->data = data;
25783842Smurray    d->aux = (long)aux;
2588097Sjkh    return list;
2598097Sjkh}
2608097Sjkh
2618097Sjkh/* Toss the items out */
2628097Sjkhvoid
26315242Sjkhitems_free(dialogMenuItem *list, int *curr, int *max)
2648097Sjkh{
26515242Sjkh    int i;
26615242Sjkh
26715242Sjkh    for (i = 0; list[i].prompt; i++) {
26815242Sjkh	safe_free(list[i].prompt);
26915242Sjkh	safe_free(list[i].title);
27015242Sjkh    }
2718097Sjkh    safe_free(list);
2728097Sjkh    *curr = *max = 0;
2738097Sjkh}
2748097Sjkh
2758556Sjkhint
27617005SjkhMkdir(char *ipath)
2778556Sjkh{
2788556Sjkh    struct stat sb;
27917005Sjkh    int final;
2808709Sjkh    char *p, *path;
2818556Sjkh
28216718Sjkh    if (file_readable(ipath) || Fake)
28315242Sjkh	return DITEM_SUCCESS;
2848709Sjkh
28517005Sjkh    path = strcpy(alloca(strlen(ipath) + 1), ipath);
2868837Sjkh    if (isDebug())
2878837Sjkh	msgDebug("mkdir(%s)\n", path);
2888556Sjkh    p = path;
2898556Sjkh    if (p[0] == '/')		/* Skip leading '/'. */
2908556Sjkh	++p;
29117005Sjkh    for (final = FALSE; !final; ++p) {
2928556Sjkh	if (p[0] == '\0' || (p[0] == '/' && p[1] == '\0'))
29317005Sjkh	    final = TRUE;
2948556Sjkh	else if (p[0] != '/')
2958556Sjkh	    continue;
2968556Sjkh	*p = '\0';
2978556Sjkh	if (stat(path, &sb)) {
2988556Sjkh	    if (errno != ENOENT) {
2998556Sjkh		msgConfirm("Couldn't stat directory %s: %s", path, strerror(errno));
30015242Sjkh		return DITEM_FAILURE;
3018556Sjkh	    }
3028837Sjkh	    if (isDebug())
3038837Sjkh		msgDebug("mkdir(%s..)\n", path);
3048556Sjkh	    if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
3058556Sjkh		msgConfirm("Couldn't create directory %s: %s", path,strerror(errno));
30615242Sjkh		return DITEM_FAILURE;
3078556Sjkh	    }
3088556Sjkh	}
3098556Sjkh	*p = '/';
3108556Sjkh    }
31115242Sjkh    return DITEM_SUCCESS;
3128556Sjkh}
3138556Sjkh
3148556Sjkhint
315107525SmarcelMkdir_command(char *key, void *dir)
316107525Smarcel{
317107525Smarcel    return (Mkdir((char*)dir));
318107525Smarcel}
319107525Smarcel
320107525Smarcelint
3218606SjkhMount(char *mountp, void *dev)
3228556Sjkh{
3238556Sjkh    struct ufs_args ufsargs;
3248606Sjkh    char device[80];
3258556Sjkh    char mountpoint[FILENAME_MAX];
3268556Sjkh
32716718Sjkh    if (Fake)
32816718Sjkh	return DITEM_SUCCESS;
32916718Sjkh
3308607Sjkh    if (*((char *)dev) != '/') {
33122931Sjkh    	sprintf(device, "%s/dev/%s", RunningAsInit ? "/mnt" : "", (char *)dev);
33222868Sjkh	sprintf(mountpoint, "%s%s", RunningAsInit ? "/mnt" : "", mountp);
3338606Sjkh    }
3348606Sjkh    else {
3358606Sjkh	strcpy(device, dev);
3368606Sjkh	strcpy(mountpoint, mountp);
3378606Sjkh    }
3388556Sjkh    memset(&ufsargs,0,sizeof ufsargs);
3398556Sjkh
34017005Sjkh    if (Mkdir(mountpoint)) {
3418709Sjkh	msgConfirm("Unable to make directory mountpoint for %s!", mountpoint);
34215242Sjkh	return DITEM_FAILURE;
3438709Sjkh    }
3448837Sjkh    if (isDebug())
3458881Srgrimes	msgDebug("mount %s %s\n", device, mountpoint);
34613428Sphk
3478556Sjkh    ufsargs.fspec = device;
34834679Sjkh    if (mount("ufs", mountpoint, RunningAsInit ? MNT_ASYNC | MNT_NOATIME : 0,
34930956Sobrien	(caddr_t)&ufsargs) == -1) {
35012661Speter	msgConfirm("Error mounting %s on %s : %s", device, mountpoint, strerror(errno));
35115242Sjkh	return DITEM_FAILURE;
3528556Sjkh    }
35315242Sjkh    return DITEM_SUCCESS;
3548556Sjkh}
35515242Sjkh
356107525Smarcelint
357107525SmarcelMount_msdosfs(char *mountp, void *dev)
358107525Smarcel{
359107525Smarcel    struct msdosfs_args mount_args;
360107525Smarcel    char device[80];
361107525Smarcel    char mountpoint[FILENAME_MAX];
362107525Smarcel
363107525Smarcel    if (Fake)
364107525Smarcel	return DITEM_SUCCESS;
365107525Smarcel
366107525Smarcel    if (*((char *)dev) != '/') {
367107525Smarcel    	sprintf(device, "%s/dev/%s", RunningAsInit ? "/mnt" : "", (char *)dev);
368107525Smarcel	sprintf(mountpoint, "%s%s", RunningAsInit ? "/mnt" : "", mountp);
369107525Smarcel    }
370107525Smarcel    else {
371107525Smarcel	strcpy(device, dev);
372107525Smarcel	strcpy(mountpoint, mountp);
373107525Smarcel    }
374107525Smarcel
375107525Smarcel    if (Mkdir(mountpoint)) {
376107525Smarcel	msgConfirm("Unable to make directory mountpoint for %s!", mountpoint);
377107525Smarcel	return DITEM_FAILURE;
378107525Smarcel    }
379107525Smarcel    if (isDebug())
380107525Smarcel	msgDebug("mount %s %s\n", device, mountpoint);
381107525Smarcel
382107525Smarcel    memset(&mount_args, 0, sizeof(mount_args));
383107525Smarcel    mount_args.fspec = device;
384107525Smarcel    mount_args.magic = MSDOSFS_ARGSMAGIC;
385107525Smarcel    mount_args.mask = S_IRWXU | S_IRWXG | S_IRWXO;
386107525Smarcel    if (mount("msdosfs", mountpoint, RunningAsInit ? MNT_ASYNC|MNT_NOATIME : 0,
387107525Smarcel	    (caddr_t)&mount_args) == -1) {
388107525Smarcel	msgConfirm("Error mounting %s on %s : %s", device, mountpoint, strerror(errno));
389107525Smarcel	return DITEM_FAILURE;
390107525Smarcel    }
391107525Smarcel    return DITEM_SUCCESS;
392107525Smarcel}
393107525Smarcel
39415242SjkhWINDOW *
39521243SjkhopenLayoutDialog(char *helpfile, char *title, int x, int y, int width, int height)
39621243Sjkh{
39722099Sjkh    WINDOW		*win;
39822099Sjkh    static char		help[FILENAME_MAX];
39922099Sjkh
40021243Sjkh    /* We need a curses window */
40121243Sjkh    win = newwin(LINES, COLS, 0, 0);
40221243Sjkh    if (win) {
40321243Sjkh	/* Say where our help comes from */
40421243Sjkh	if (helpfile) {
40522099Sjkh	    use_helpline("Press F1 for more information on this screen.");
40622099Sjkh	    use_helpfile(systemHelpFile(helpfile, help));
40721243Sjkh	}
40821243Sjkh	/* Setup a nice screen for us to splat stuff onto */
40921243Sjkh	draw_box(win, y, x, height, width, dialog_attr, border_attr);
41021243Sjkh	wattrset(win, dialog_attr);
41123588Sjkh	mvwaddstr(win, y, x + (COLS - strlen(title)) / 2, title);
41221243Sjkh    }
41321243Sjkh    return win;
41421243Sjkh}
41521243Sjkh
41621243SjkhComposeObj *
41721243SjkhinitLayoutDialog(WINDOW *win, Layout *layout, int x, int y, int *max)
41821243Sjkh{
41921243Sjkh    ComposeObj *obj = NULL, *first;
42021243Sjkh    int n;
42121243Sjkh
42221243Sjkh    /* Loop over the layout list, create the objects, and add them
42321243Sjkh       onto the chain of objects that dialog uses for traversal*/
42421243Sjkh
42521243Sjkh    n = 0;
42621243Sjkh    while (layout[n].help != NULL) {
42722099Sjkh	int t = TYPE_OF_OBJ(layout[n].type);
42822099Sjkh
42922099Sjkh	switch (t) {
43021243Sjkh	case STRINGOBJ:
43121243Sjkh	    layout[n].obj = NewStringObj(win, layout[n].prompt, layout[n].var,
43221243Sjkh					 layout[n].y + y, layout[n].x + x, layout[n].len, layout[n].maxlen);
43322099Sjkh	    ((StringObj *)layout[n].obj)->attr_mask = ATTR_OF_OBJ(layout[n].type);
43421243Sjkh	    break;
43521243Sjkh
43621243Sjkh	case BUTTONOBJ:
43721243Sjkh	    layout[n].obj = NewButtonObj(win, layout[n].prompt, layout[n].var, layout[n].y + y, layout[n].x + x);
43821243Sjkh	    break;
43921243Sjkh
44021243Sjkh	default:
44121243Sjkh	    msgFatal("Don't support this object yet!");
44221243Sjkh	}
44322099Sjkh	AddObj(&obj, t, (void *) layout[n].obj);
44421243Sjkh	n++;
44521243Sjkh    }
44621243Sjkh    *max = n - 1;
44721243Sjkh    /* Find the first object in the list */
44821243Sjkh    for (first = obj; first->prev; first = first->prev);
44921243Sjkh    return first;
45021243Sjkh}
45121243Sjkh
45221243Sjkhint
45321243SjkhlayoutDialogLoop(WINDOW *win, Layout *layout, ComposeObj **obj, int *n, int max, int *cbutton, int *cancel)
45421243Sjkh{
45521243Sjkh    char help_line[80];
45621243Sjkh    int ret, i, len = strlen(layout[*n].help);
45721243Sjkh
45821243Sjkh    /* Display the help line at the bottom of the screen */
45921243Sjkh    for (i = 0; i < 79; i++)
46021243Sjkh	help_line[i] = (i < len) ? layout[*n].help[i] : ' ';
46121243Sjkh    help_line[i] = '\0';
46221243Sjkh    use_helpline(help_line);
46321243Sjkh    display_helpline(win, LINES - 1, COLS - 1);
46422099Sjkh    wrefresh(win);
46521243Sjkh
46621243Sjkh    /* Ask for libdialog to do its stuff */
46721243Sjkh    ret = PollObj(obj);
46821243Sjkh    /* Handle special case stuff that libdialog misses. Sigh */
46921243Sjkh    switch (ret) {
47021243Sjkh    case SEL_ESC:	/* Bail out */
47121243Sjkh	*cancel = TRUE;
47221243Sjkh	return FALSE;
47321243Sjkh
47421243Sjkh	/* This doesn't work for list dialogs. Oh well. Perhaps
47521243Sjkh	   should special case the move from the OK button ``up''
47621243Sjkh	   to make it go to the interface list, but then it gets
47721243Sjkh	   awkward for the user to go back and correct screw up's
47821243Sjkh	   in the per-interface section */
47921243Sjkh    case KEY_DOWN:
48021243Sjkh    case SEL_CR:
48121243Sjkh    case SEL_TAB:
48221243Sjkh	if (*n < max)
48321243Sjkh	    ++*n;
48421243Sjkh	else
48521243Sjkh	    *n = 0;
48621243Sjkh	break;
48721243Sjkh
48821243Sjkh	/* The user has pressed enter over a button object */
48921243Sjkh    case SEL_BUTTON:
49021243Sjkh	if (cbutton && *cbutton)
49121243Sjkh	    *cancel = TRUE;
49221243Sjkh	else
49321243Sjkh	    *cancel = FALSE;
49421243Sjkh	return FALSE;
49521243Sjkh
49621243Sjkh    case KEY_UP:
49721243Sjkh    case SEL_BACKTAB:
49821243Sjkh	if (*n)
49921243Sjkh	    --*n;
50021243Sjkh	else
50121243Sjkh	    *n = max;
50221243Sjkh	break;
50321243Sjkh
50421243Sjkh    case KEY_F(1):
50521243Sjkh	display_helpfile();
50621243Sjkh
50721243Sjkh	/* They tried some key combination we don't support - tootle them forcefully! */
50821243Sjkh    default:
50921243Sjkh	beep();
51021243Sjkh    }
51121243Sjkh    return TRUE;
51221243Sjkh}
51321243Sjkh
51421243SjkhWINDOW *
51515242Sjkhsavescr(void)
51615242Sjkh{
51715242Sjkh    WINDOW *w;
51815242Sjkh
51915242Sjkh    w = dupwin(newscr);
52015242Sjkh    return w;
52115242Sjkh}
52215242Sjkh
52315242Sjkhvoid
52415242Sjkhrestorescr(WINDOW *w)
52515242Sjkh{
52615242Sjkh    touchwin(w);
52715242Sjkh    wrefresh(w);
52815242Sjkh    delwin(w);
52915242Sjkh}
53021243Sjkh
531160380Ssimon/*
532160380Ssimon * Get a sysctl variable as a string or "<unknown>" if sysctl fails.
533160380Ssimon * Caller must free returned string.
534160380Ssimon */
535160380Ssimonchar *
536160380Ssimongetsysctlbyname(const char *sysctlname)
537160380Ssimon{
538160380Ssimon    char *buf;
539160380Ssimon    size_t sz, buf_sz = 0;
540160380Ssimon    const char unk_str[] = "<unknown>";
541160380Ssimon
542160380Ssimon    sysctlbyname(sysctlname, NULL, &buf_sz, NULL, 0);
543160380Ssimon    buf_sz = MAX(sizeof(unk_str), buf_sz) + 1;
544160380Ssimon    sz = buf_sz - 1;
545160380Ssimon    buf = (char *)safe_malloc(buf_sz);
546160380Ssimon
547160380Ssimon    if (sysctlbyname(sysctlname, buf, &sz, NULL, 0) != -1)
548160380Ssimon	buf[sz] = '\0';
549160380Ssimon    else
550160380Ssimon	strlcpy(buf, unk_str, buf_sz);
551160380Ssimon
552160380Ssimon    return buf;
553160380Ssimon}
554