1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 /* 32 * Copyright (c) 2018, Joyent, Inc. 33 */ 34 35 #include <stdio.h> 36 #include <ctype.h> 37 #include <string.h> 38 #include <signal.h> 39 #include <valtools.h> 40 #include <stdlib.h> 41 #include <locale.h> 42 #include <libintl.h> 43 #include <limits.h> 44 #include <wchar.h> 45 #include "usage.h" 46 #include "libadm.h" 47 48 #define BADPID (-2) 49 #define INVISMAXSIZE 36 50 51 static char *prog; 52 static char *deflt = NULL, *prompt = NULL, *error = NULL, *help = NULL; 53 static int kpid = BADPID; 54 static int signo; 55 56 static char *label, **invis; 57 static int ninvis = 0; 58 static int max = 1; 59 static int attr = CKALPHA; 60 61 #define MAXSIZE 128 62 #define LSIZE 1024 63 #define INTERR \ 64 "%s: ERROR: internal error occurred while attempting menu setup\n" 65 #define MYOPTS \ 66 "\t-f file #file containing choices\n" \ 67 "\t-l label #menu label\n" \ 68 "\t-i invis [, ...] #invisible menu choices\n" \ 69 "\t-m max #maximum choices user may select\n" \ 70 "\t-n #do not sort choices alphabetically\n" \ 71 "\t-o #don't prompt if only one choice\n" \ 72 "\t-u #unnumbered choices\n" 73 74 static const char husage[] = "Wh"; 75 static const char eusage[] = "We"; 76 77 static void 78 usage(void) 79 { 80 switch (*prog) { 81 default: 82 (void) fprintf(stderr, 83 gettext("usage: %s [options] [choice [...]]\n"), prog); 84 (void) fprintf(stderr, gettext(OPTMESG)); 85 (void) fprintf(stderr, gettext(MYOPTS)); 86 (void) fprintf(stderr, gettext(STDOPTS)); 87 break; 88 89 case 'h': 90 (void) fprintf(stderr, 91 gettext("usage: %s [options] [choice [...]]\n"), prog); 92 (void) fprintf(stderr, gettext(OPTMESG)); 93 (void) fprintf(stderr, 94 gettext("\t-W width\n\t-h help\n")); 95 break; 96 97 case 'e': 98 (void) fprintf(stderr, 99 gettext("usage: %s [options] [choice [...]]\n"), prog); 100 (void) fprintf(stderr, gettext(OPTMESG)); 101 (void) fprintf(stderr, 102 gettext("\t-W width\n\t-e error\n")); 103 break; 104 } 105 exit(1); 106 } 107 108 /* 109 * Given argv[0], return a pointer to the basename of the program. 110 */ 111 static char * 112 prog_name(char *arg0) 113 { 114 char *str; 115 116 /* first strip trailing '/' characters (exec() allows these!) */ 117 str = arg0 + strlen(arg0); 118 while (str > arg0 && *--str == '/') 119 *str = '\0'; 120 if ((str = strrchr(arg0, '/')) != NULL) 121 return (str + 1); 122 return (arg0); 123 } 124 125 int 126 main(int argc, char **argv) 127 { 128 CKMENU *mp; 129 FILE *fp = NULL; 130 int c, i; 131 char **item; 132 char temp[LSIZE * MB_LEN_MAX]; 133 size_t mmax; 134 size_t invismaxsize = INVISMAXSIZE; 135 size_t n, r; 136 wchar_t wline[LSIZE], wtemp[LSIZE]; 137 138 (void) setlocale(LC_ALL, ""); 139 140 #if !defined(TEXT_DOMAIN) 141 #define TEXT_DOMAIN "SYS_TEST" 142 #endif 143 (void) textdomain(TEXT_DOMAIN); 144 145 prog = prog_name(argv[0]); 146 147 invis = (char **)calloc(invismaxsize, sizeof (char *)); 148 if (!invis) { 149 (void) fprintf(stderr, 150 gettext("Not enough memory\n")); 151 exit(1); 152 } 153 while ((c = getopt(argc, argv, "m:oni:l:f:ud:p:e:h:k:s:QW:?")) != EOF) { 154 /* check for invalid option */ 155 if ((*prog == 'e') && !strchr(eusage, c)) 156 usage(); /* no valid options */ 157 if ((*prog == 'h') && !strchr(husage, c)) 158 usage(); 159 160 switch (c) { 161 case 'Q': 162 ckquit = 0; 163 break; 164 165 case 'W': 166 ckwidth = atol(optarg); 167 if (ckwidth < 0) { 168 (void) fprintf(stderr, 169 gettext("%s: ERROR: negative display width specified\n"), 170 prog); 171 exit(1); 172 } 173 break; 174 175 case 'm': 176 max = atoi(optarg); 177 if (max > SHRT_MAX || max < SHRT_MIN) { 178 (void) fprintf(stderr, 179 gettext("%s: ERROR: too large or too small max value specified\n"), 180 prog); 181 exit(1); 182 } 183 break; 184 185 case 'o': 186 attr |= CKONEFLAG; 187 break; 188 189 case 'n': 190 attr &= ~CKALPHA; 191 break; 192 193 case 'i': 194 invis[ninvis++] = optarg; 195 if (ninvis == invismaxsize) { 196 invismaxsize += INVISMAXSIZE; 197 invis = (char **)realloc(invis, 198 invismaxsize * sizeof (char *)); 199 if (!invis) { 200 (void) fprintf(stderr, 201 gettext("Not enough memory\n")); 202 exit(1); 203 } 204 (void) memset(invis + ninvis, 0, 205 (invismaxsize - ninvis) * 206 sizeof (char *)); 207 } 208 break; 209 210 case 'l': 211 label = optarg; 212 break; 213 214 case 'f': 215 if ((fp = fopen(optarg, "r")) == NULL) { 216 (void) fprintf(stderr, 217 gettext("%s: ERROR: can't open %s\n"), 218 prog, optarg); 219 exit(1); 220 } 221 break; 222 223 case 'u': 224 attr |= CKUNNUM; 225 break; 226 227 case 'd': 228 deflt = optarg; 229 break; 230 231 case 'p': 232 prompt = optarg; 233 break; 234 235 case 'e': 236 error = optarg; 237 break; 238 239 case 'h': 240 help = optarg; 241 break; 242 243 case 'k': 244 kpid = atoi(optarg); 245 break; 246 247 case 's': 248 signo = atoi(optarg); 249 break; 250 251 default: 252 usage(); 253 } 254 } 255 256 if (signo) { 257 if (kpid == BADPID) 258 usage(); 259 } else 260 signo = SIGTERM; 261 262 mp = allocmenu(label, attr); 263 if (fp) { 264 *wtemp = L'\0'; 265 while (fgetws(wline, LSIZE, fp)) { 266 /* 267 * Skip comment lines, those beginning with '#'. 268 * Note: AT&T forgot this & needs the next 2 lines. 269 */ 270 if (*wline == L'#') 271 continue; 272 n = wcslen(wline); 273 if ((n != 0) && (wline[n - 1] == L'\n')) 274 wline[n - 1] = L'\0'; 275 /* 276 * if the line begins with a space character, 277 * this is a continuous line to the previous line. 278 */ 279 if (iswspace(*wline)) { 280 (void) wcscat(wtemp, L"\n"); 281 (void) wcscat(wtemp, wline); 282 } else { 283 if (*wtemp) { 284 n = wcslen(wtemp); 285 r = wcstombs(temp, wtemp, 286 n * MB_LEN_MAX); 287 if (r == (size_t)-1) { 288 (void) fprintf(stderr, 289 gettext("Invalid character in the menu definition.\n")); 290 exit(1); 291 } 292 if (setitem(mp, temp)) { 293 (void) fprintf(stderr, 294 gettext(INTERR), prog); 295 exit(1); 296 } 297 } 298 (void) wcscpy(wtemp, wline); 299 } 300 } 301 if (*wtemp) { 302 n = wcslen(wtemp); 303 r = wcstombs(temp, wtemp, n * MB_LEN_MAX); 304 if (r == (size_t)-1) { 305 (void) fprintf(stderr, 306 gettext("Invalid character in the menu definition.\n")); 307 exit(1); 308 } 309 if (setitem(mp, temp)) { 310 (void) fprintf(stderr, gettext(INTERR), prog); 311 exit(1); 312 } 313 } 314 } 315 316 while (optind < argc) { 317 if (setitem(mp, argv[optind++])) { 318 (void) fprintf(stderr, gettext(INTERR), prog); 319 exit(1); 320 } 321 } 322 323 for (n = 0; n < ninvis; ) { 324 if (setinvis(mp, invis[n++])) { 325 (void) fprintf(stderr, gettext(INTERR), prog); 326 exit(1); 327 } 328 } 329 330 if (*prog == 'e') { 331 ckindent = 0; 332 ckitem_err(mp, error); 333 exit(0); 334 } else if (*prog == 'h') { 335 ckindent = 0; 336 ckitem_hlp(mp, help); 337 exit(0); 338 } 339 340 if (max < 1) { 341 mmax = mp->nchoices; 342 } else { 343 mmax = max; 344 } 345 346 /* 347 * if -o option is specified, mp->nchoices is 1, and if no invisible 348 * item is specified, ckitem() will consume two entries of item, 349 * even though 'max' is set to 1. So to take care of that problem, we 350 * allocate one extra element for item 351 */ 352 item = (char **)calloc(mmax+1, sizeof (char *)); 353 if (!item) { 354 (void) fprintf(stderr, 355 gettext("Not enough memory\n")); 356 exit(1); 357 } 358 n = ckitem(mp, item, max, deflt, error, help, prompt); 359 if (n == 3) { 360 if (kpid > -2) 361 (void) kill(kpid, signo); 362 (void) puts("q"); 363 } else if (n == 0) { 364 i = 0; 365 while (item[i]) 366 (void) puts(item[i++]); 367 } 368 return (n); 369 } 370