]> git.ipfire.org Git - thirdparty/newt.git/blame - whiptail.c
fix automatic width in whiptail with unicode characters
[thirdparty/newt.git] / whiptail.c
CommitLineData
feef2cb5 1#include "config.h"
9be6d60e 2#include <fcntl.h>
6c049389 3#include <popt.h>
9be6d60e 4#include <stdio.h>
5#include <string.h>
6#include <stdlib.h>
feef2cb5 7#include <signal.h>
9be6d60e 8#include <unistd.h>
feef2cb5 9#include <wchar.h>
10#include <slang.h>
83fe9fde 11#include <sys/stat.h>
9be6d60e 12
feef2cb5 13#include "nls.h"
649a0152 14#include "dialogboxes.h"
9be6d60e 15#include "newt.h"
feef2cb5 16#include "newt_pr.h"
17
18enum { NO_ERROR = 0, WAS_ERROR = 1 };
9be6d60e 19
139f06bc 20enum mode { MODE_NONE, MODE_INFOBOX, MODE_MSGBOX, MODE_YESNO, MODE_CHECKLIST,
feef2cb5 21 MODE_INPUTBOX, MODE_RADIOLIST, MODE_MENU, MODE_GAUGE ,
22 MODE_TEXTBOX, MODE_PASSWORDBOX};
9be6d60e 23
6f481af2 24#define OPT_MSGBOX 1000
25#define OPT_CHECKLIST 1001
26#define OPT_YESNO 1002
27#define OPT_INPUTBOX 1003
28#define OPT_FULLBUTTONS 1004
29#define OPT_MENU 1005
30#define OPT_RADIOLIST 1006
31#define OPT_GAUGE 1007
32#define OPT_INFOBOX 1008
feef2cb5 33#define OPT_TEXTBOX 1009
34#define OPT_PASSWORDBOX 1010
35
36static void usage(int err) {
37 newtFinished();
38 fprintf (err ? stderr : stdout,
39 _("Box options: \n"
40 "\t--msgbox <text> <height> <width>\n"
41 "\t--yesno <text> <height> <width>\n"
42 "\t--infobox <text> <height> <width>\n"
43 "\t--inputbox <text> <height> <width> [init] \n"
44 "\t--passwordbox <text> <height> <width> [init] \n"
45 "\t--textbox <file> <height> <width>\n"
46 "\t--menu <text> <height> <width> <listheight> [tag item] ...\n"
47 "\t--checklist <text> <height> <width> <listheight> [tag item status]...\n"
30cfd38a 48 "\t--radiolist <text> <height> <width> <listheight> [tag item status]...\n"
feef2cb5 49 "\t--gauge <text> <height> <width> <percent>\n"
50 "Options: (depend on box-option)\n"
51 "\t--clear clear screen on exit\n"
45bffa7f 52 "\t--defaultno default no button\n"
feef2cb5 53 "\t--default-item <string> set default string\n"
6c7007a6 54 "\t--fb, --fullbuttons use full buttons\n"
feef2cb5 55 "\t--nocancel no cancel button\n"
60d3c50b 56 "\t--yes-button <text> set text of yes button\n"
57 "\t--no-button <text> set text of no button\n"
58 "\t--ok-button <text> set text of ok button\n"
59 "\t--cancel-button <text> set text of cancel button\n"
6c7007a6
ML
60 "\t--noitem don't display items\n"
61 "\t--notags don't display tags\n"
f0e9344a 62 "\t--separate-output output one line at a time\n"
feef2cb5 63 "\t--output-fd <fd> output to fd, not stdout\n"
64 "\t--title <title> display title\n"
65 "\t--backtitle <backtitle> display backtitle\n"
c3a3b1e8 66 "\t--scrolltext force vertical scrollbars\n"
6c7007a6
ML
67 "\t--topleft put window in top-left corner\n"
68 "\t-h, --help print this message\n"
69 "\t-v, --version print version information\n\n"));
feef2cb5 70 exit(err ? DLG_ERROR : 0 );
71}
72
73static void print_version(void) {
74 fprintf (stdout, _("whiptail (newt): %s\n"), VERSION);
75}
76
feef2cb5 77#if 0
78/* FIXME Copied from newt.c
79 * Place somewhere better -- dialogboxes? -- amck
80 */
81int wstrlen(const char *str, int len) {
82 mbstate_t ps;
83 wchar_t tmp;
84 int nchars = 0;
85
86 if (!str) return 0;
87 if (!len) return 0;
88 if (len < 0) len = strlen(str);
89 memset(&ps,0,sizeof(mbstate_t));
90 while (len > 0) {
91 int x,y;
92
93 x = mbrtowc(&tmp,str,len,&ps);
94 if (x >0) {
95 str += x;
96 len -= x;
97 y = wcwidth(tmp);
98 if (y>0)
99 nchars+=y;
100 } else break;
101 }
102 return nchars;
103}
104#endif
105
106/*
107 * The value of *width is increased if it is not as large as the width of
108 * the line.
109 */
110static const char * lineWidth(int * width, const char * line, int *chrs)
111{
112 const char * s = line;
113
114 if ( line == NULL )
115 return 0;
116
117 while ( *s != '\0' && *s != '\n' )
118 s++;
119
120 if ( *s == '\n' )
121 s++;
122
123 *chrs = _newt_wstrlen (line, s - line );
124 *width = max(*width, *chrs);
9be6d60e 125
feef2cb5 126 return s;
127}
128
129
130/*
131 * cleanNewlines
132 * Handle newlines in text. Hack.
133 */
134void cleanNewlines (char *text)
135{
ba84e71b
ML
136 char *p, *q;
137
138 for (p = q = text; *p; p++, q++)
139 if (*p == '\\' && p[1] == 'n') {
140 p++;
141 *q = '\n';
142 } else
143 *q = *p;
144 *q = '\0';
feef2cb5 145}
146
147/*
148 * The height of a text string is added to height, and width is increased
149 * if it is not big enough to store the text string.
150 */
151static const char * textSize(int * height, int * width,
152 int maxWidth,
153 const char * text)
154{
155 int h = 0;
156 int w = 0;
157 int chrs = 0;
158
159
160 if ( text == NULL )
161 return 0;
162
163 while ( *text != '\0' ) {
164 h++;
165 text = lineWidth(width, text, &chrs);
166 /* Allow for text overflowing. May overestimate a bit */
167 h += chrs / maxWidth;
168 }
169
170 h += 2;
171 w += 2;
172
173 *height += h;
174 *width += w;
175
176 *width = min(*width, maxWidth);
177 return text;
178}
179
180/*
181 * Add space for buttons.
182 * NOTE: when this is internationalized, the button width might change.
183 */
184static void spaceForButtons(int * height, int * width, int count, int full) {
185 /* Make space for the buttons */
186 if ( full ) {
187 *height += 4;
188 if ( count == 1 )
189 *width = max(*width, 7);
190 else
191 *width = max(*width, 20);
192 }
193 else {
194 *height += 2;
195 if ( count == 1 )
196 *width = max(*width, 7);
197 else
198 *width = max(*width, 19);
199 }
200}
201
9c4eb622
ML
202static int menuSize(int * height, int * width, int * listHeight,
203 enum mode mode, poptContext options) {
c00a8d1b 204 const char ** argv = poptGetArgs(options);
feef2cb5 205 int h = 0;
206 int tagWidth = 0;
207 int descriptionWidth = 0;
208 int overhead = 10;
feef2cb5 209
210 if ( argv == 0 || *argv == 0 )
211 return 0;
212
feef2cb5 213 if ( mode == MODE_MENU )
214 overhead = 5;
215
216 while ( argv[0] != 0 && argv[1] ) {
ce98a323
ML
217 tagWidth = max(tagWidth, _newt_wstrlen(argv[0], -1));
218 descriptionWidth = max(descriptionWidth, _newt_wstrlen(argv[1], -1));
feef2cb5 219
220 if ( mode == MODE_MENU )
221 argv += 2;
222 else
223 argv += 3;
224 h++;
225 }
226
227 *width = max(*width, tagWidth + descriptionWidth + overhead);
228 *width = min(*width, SLtt_Screen_Cols);
229
230 h = min(h, SLtt_Screen_Rows - *height - 4);
231 *height = *height + h + 1;
9c4eb622 232 *listHeight = h;
feef2cb5 233 return 0;
234}
235
236/*
237 * Guess the size of a window, given what will be displayed within it.
238 */
9c4eb622
ML
239static void guessSize(int * height, int * width, int * listHeight,
240 enum mode mode, int * flags, int fullButtons,
feef2cb5 241 const char * title, const char * text,
242 poptContext options) {
243
244 int w = 0, h = 0, chrs = 0;
245
246 textSize(&h, &w, SLtt_Screen_Cols -4 , text); /* Width and height for text */
247 lineWidth(&w, title, &chrs); /* Width for title */
248
249 if ( w > 0 )
250 w += 4;
251
252 switch ( mode ) {
253 case MODE_CHECKLIST:
254 case MODE_RADIOLIST:
255 case MODE_MENU:
256 spaceForButtons(&h, &w, *flags & FLAG_NOCANCEL ? 1 : 2,
257 fullButtons);
9c4eb622 258 menuSize(&h, &w, listHeight, mode, options);
feef2cb5 259 break;
260 case MODE_YESNO:
261 case MODE_MSGBOX:
262 spaceForButtons(&h, &w, 1, fullButtons);
263 break;
264 case MODE_INPUTBOX:
265 spaceForButtons(&h, &w, *flags & FLAG_NOCANCEL ? 1 : 2,
266 fullButtons);
267 h += 1;
268 break;
269 case MODE_GAUGE:
270 h += 2;
271 break;
272 case MODE_NONE:
273 break;
274 default:
275 break;
276 };
277
278 /*
279 * Fixed window-border overhead.
280 * NOTE: This will change if we add a way to turn off drop-shadow and/or
281 * box borders. That would be desirable for display-sized screens.
282 */
283 w += 2;
284 h += 2;
285
286 if ( h > SLtt_Screen_Rows - 1 ) {
287 h = SLtt_Screen_Rows - 1;
288 *flags |= FLAG_SCROLL_TEXT;
289 w += 2; /* Add width of slider - is this right? */
290 }
291
292 *width = min(max(*width, w), SLtt_Screen_Cols);
293 *height = max(*height, h);
9be6d60e 294}
295
585f5503 296char *
297readTextFile(const char * filename)
298{
299 int fd = open(filename, O_RDONLY, 0);
300 struct stat s;
301 char * buf;
302
303 if ( fd < 0 || fstat(fd, &s) != 0 ) {
304 perror(filename);
305 exit(DLG_ERROR);
306 }
307
3341bdc5 308 if ( (buf = malloc(s.st_size + 1)) == 0 ) {
feef2cb5 309 fprintf(stderr, _("%s: too large to display.\n"), filename);
3341bdc5
ML
310 exit(DLG_ERROR);
311 }
585f5503 312
313 if ( read(fd, buf, s.st_size) != s.st_size ) {
314 perror(filename);
315 exit(DLG_ERROR);
316 }
317 close(fd);
659af8f0 318 buf[s.st_size] = '\0';
585f5503 319 return buf;
320}
321
eb0a0545 322int main(int argc, const char ** argv) {
9be6d60e 323 enum mode mode = MODE_NONE;
324 poptContext optCon;
325 int arg;
feef2cb5 326 char * text;
eb0a0545 327 const char * nextArg;
9be6d60e 328 char * end;
329 int height;
330 int width;
bb5f279b 331 int listHeight = 1;
649a0152 332 int fd = -1;
333 int needSpace = 0;
9be6d60e 334 int noCancel = 0;
57cb49e7 335 int noTags = 0;
9be6d60e 336 int noItem = 0;
337 int clear = 0;
338 int scrollText = 0;
feef2cb5 339 int rc = DLG_CANCEL;
9be6d60e 340 int flags = 0;
ff3d94b9 341 int defaultNo = 0;
9be6d60e 342 int separateOutput = 0;
feef2cb5 343 int fullButtons = 0;
9623bfae 344 int outputfd = 2;
45bffa7f 345 int topLeft = 0;
638f672c 346 FILE *output;
f40f18ae
ML
347 char * result;
348 char ** selections, ** next;
9be6d60e 349 char * title = NULL;
feef2cb5 350 char *default_item = NULL;
9be6d60e 351 char * backtitle = NULL;
60d3c50b 352 char * yes_button = NULL;
353 char * no_button = NULL;
354 char * ok_button = NULL;
355 char * cancel_button = NULL;
feef2cb5 356 int help = 0, version = 0;
9be6d60e 357 struct poptOption optionsTable[] = {
6f481af2 358 { "backtitle", '\0', POPT_ARG_STRING, &backtitle, 0 },
359 { "checklist", '\0', 0, 0, OPT_CHECKLIST },
360 { "clear", '\0', 0, &clear, 0 },
361 { "defaultno", '\0', 0, &defaultNo, 0 },
362 { "inputbox", '\0', 0, 0, OPT_INPUTBOX },
363 { "fb", '\0', 0, 0, OPT_FULLBUTTONS },
364 { "fullbuttons", '\0', 0, 0, OPT_FULLBUTTONS },
365 { "gauge", '\0', 0, 0, OPT_GAUGE },
366 { "infobox", '\0', 0, 0, OPT_INFOBOX },
367 { "menu", '\0', 0, 0, OPT_MENU },
368 { "msgbox", '\0', 0, 0, OPT_MSGBOX },
369 { "nocancel", '\0', 0, &noCancel, 0 },
370 { "noitem", '\0', 0, &noItem, 0 },
feef2cb5 371 { "default-item", '\0', POPT_ARG_STRING, &default_item, 0},
6f481af2 372 { "notags", '\0', 0, &noTags, 0 },
373 { "radiolist", '\0', 0, 0, OPT_RADIOLIST },
374 { "scrolltext", '\0', 0, &scrollText, 0 },
375 { "separate-output", '\0', 0, &separateOutput, 0 },
376 { "title", '\0', POPT_ARG_STRING, &title, 0 },
6f481af2 377 { "textbox", '\0', 0, 0, OPT_TEXTBOX },
45bffa7f 378 { "topleft", '\0', 0, &topLeft, 0 },
feef2cb5 379 { "yesno", '\0', 0, 0, OPT_YESNO },
380 { "passwordbox", '\0', 0, 0, OPT_PASSWORDBOX },
381 { "output-fd", '\0', POPT_ARG_INT, &outputfd, 0 },
60d3c50b 382 { "yes-button", '\0', POPT_ARG_STRING, &yes_button, 0},
383 { "no-button", '\0', POPT_ARG_STRING, &no_button, 0},
384 { "ok-button", '\0', POPT_ARG_STRING, &ok_button, 0},
385 { "cancel-button", '\0', POPT_ARG_STRING, &cancel_button, 0},
feef2cb5 386 { "help", 'h', 0, &help, 0, NULL, NULL },
387 { "version", 'v', 0, &version, 0, NULL, NULL },
6f481af2 388 { 0, 0, 0, 0, 0 }
9be6d60e 389 };
feef2cb5 390
970ce9d5 391#ifdef ENABLE_NLS
feef2cb5 392 setlocale (LC_ALL, "");
393 bindtextdomain (PACKAGE, LOCALEDIR);
394 textdomain (PACKAGE);
970ce9d5 395#endif
feef2cb5 396
649a0152 397 optCon = poptGetContext("whiptail", argc, argv, optionsTable, 0);
9be6d60e 398
399 while ((arg = poptGetNextOpt(optCon)) > 0) {
6f481af2 400 switch (arg) {
401 case OPT_INFOBOX:
feef2cb5 402 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 403 mode = MODE_INFOBOX;
404 break;
405
406 case OPT_MENU:
feef2cb5 407 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 408 mode = MODE_MENU;
409 break;
410
411 case OPT_MSGBOX:
feef2cb5 412 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 413 mode = MODE_MSGBOX;
414 break;
feef2cb5 415
416 case OPT_TEXTBOX:
417 if (mode != MODE_NONE) usage(WAS_ERROR);
418 mode = MODE_TEXTBOX;
419 break;
420
421 case OPT_PASSWORDBOX:
422 if (mode != MODE_NONE) usage(WAS_ERROR);
423 mode = MODE_PASSWORDBOX;
424 break;
425
6f481af2 426 case OPT_RADIOLIST:
feef2cb5 427 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 428 mode = MODE_RADIOLIST;
429 break;
430
431 case OPT_CHECKLIST:
feef2cb5 432 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 433 mode = MODE_CHECKLIST;
434 break;
435
436 case OPT_FULLBUTTONS:
feef2cb5 437 fullButtons = 1;
6f481af2 438 useFullButtons(1);
439 break;
440
441 case OPT_YESNO:
feef2cb5 442 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 443 mode = MODE_YESNO;
444 break;
445
446 case OPT_GAUGE:
feef2cb5 447 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 448 mode = MODE_GAUGE;
449 break;
450
451 case OPT_INPUTBOX:
feef2cb5 452 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 453 mode = MODE_INPUTBOX;
454 break;
455 }
9be6d60e 456 }
457
feef2cb5 458 if (help) {
459 usage(NO_ERROR);
460 exit(0);
461 }
462 if (version) {
463 print_version();
464 exit(0);
465 }
466
9be6d60e 467 if (arg < -1) {
6f481af2 468 fprintf(stderr, "%s: %s\n",
469 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
470 poptStrerror(arg));
471 exit(1);
9be6d60e 472 }
473
9623bfae 474 output = fdopen (outputfd, "w");
475 if (output == NULL ) {
476 perror ("Cannot open output-fd\n");
477 exit (DLG_ERROR);
478 }
479
feef2cb5 480 if (mode == MODE_NONE) usage(WAS_ERROR);
9be6d60e 481
c00a8d1b 482 if (!(nextArg = poptGetArg(optCon))) usage(WAS_ERROR);
483 text = strdup(nextArg);
9be6d60e 484
3341bdc5
ML
485 if (mode == MODE_TEXTBOX) {
486 char *t = text;
487 text = readTextFile(t);
488 free(t);
489 }
585f5503 490
feef2cb5 491 if (!(nextArg = poptGetArg(optCon))) usage(WAS_ERROR);
9be6d60e 492 height = strtoul(nextArg, &end, 10);
feef2cb5 493 if (*end) usage(WAS_ERROR);
9be6d60e 494
feef2cb5 495 if (!(nextArg = poptGetArg(optCon))) usage(WAS_ERROR);
9be6d60e 496 width = strtoul(nextArg, &end, 10);
feef2cb5 497 if (*end) usage(WAS_ERROR);
9be6d60e 498
9c4eb622
ML
499 switch (mode) {
500 case MODE_MENU:
501 case MODE_RADIOLIST:
502 case MODE_CHECKLIST:
503 if (!(nextArg = poptGetArg(optCon))) usage(WAS_ERROR);
504 listHeight = strtoul(nextArg, &end, 10);
505 if (*end) usage(WAS_ERROR);
506 break;
507
508 case MODE_GAUGE:
6f481af2 509 fd = dup(0);
3341bdc5
ML
510 if (fd < 0 || close(0) < 0) {
511 perror("dup/close stdin");
512 exit(DLG_ERROR);
513 }
514 if (open("/dev/tty", O_RDWR) != 0) {
515 perror("open /dev/tty");
516 exit(DLG_ERROR);
517 }
9c4eb622 518 break;
9be6d60e 519 }
520
521 newtInit();
522 newtCls();
feef2cb5 523
524 cleanNewlines(text);
525
73bb4086 526 if (height <= 0 || width <= 0 || listHeight <= 0) {
9c4eb622
ML
527 guessSize(&height, &width, &listHeight, mode, &flags, fullButtons,
528 title, text, optCon);
73bb4086
ML
529 if (!topLeft && backtitle && SLtt_Screen_Rows > 10 &&
530 height + 1 == SLtt_Screen_Rows &&
531 _newt_wstrlen(backtitle, -1) > (SLtt_Screen_Cols - width) / 2) {
532 height -= 1;
533 listHeight -= 1;
534 }
535 }
feef2cb5 536
9be6d60e 537 width -= 2;
538 height -= 2;
9be6d60e 539
45bffa7f 540 newtOpenWindow(topLeft ? 1 : (SLtt_Screen_Cols - width) / 2,
541 topLeft ? 1 : (SLtt_Screen_Rows - height) / 2, width, height, title);
9be6d60e 542 if (backtitle)
6f481af2 543 newtDrawRootText(0, 0, backtitle);
9be6d60e 544
60d3c50b 545 if (ok_button)
546 setButtonText(ok_button, BUTTON_OK);
547 if (cancel_button)
548 setButtonText(cancel_button, BUTTON_CANCEL);
549 if (yes_button)
550 setButtonText(yes_button, BUTTON_YES);
551 if (no_button)
552 setButtonText(no_button, BUTTON_NO);
553
9be6d60e 554 if (noCancel) flags |= FLAG_NOCANCEL;
555 if (noItem) flags |= FLAG_NOITEM;
57cb49e7 556 if (noTags) flags |= FLAG_NOTAGS;
9be6d60e 557 if (scrollText) flags |= FLAG_SCROLL_TEXT;
ff3d94b9 558 if (defaultNo) flags |= FLAG_DEFAULT_NO;
9be6d60e 559
560 switch (mode) {
561 case MODE_MSGBOX:
585f5503 562 case MODE_TEXTBOX:
6f481af2 563 rc = messageBox(text, height, width, MSGBOX_MSG, flags);
564 break;
9be6d60e 565
139f06bc 566 case MODE_INFOBOX:
6f481af2 567 rc = messageBox(text, height, width, MSGBOX_INFO, flags);
568 break;
139f06bc 569
9be6d60e 570 case MODE_YESNO:
6f481af2 571 rc = messageBox(text, height, width, MSGBOX_YESNO, flags);
572 break;
9be6d60e 573
574 case MODE_INPUTBOX:
6f481af2 575 rc = inputBox(text, height, width, optCon, flags, &result);
f40f18ae
ML
576 if (rc == DLG_OKAY) {
577 fprintf(output, "%s", result);
578 free(result);
579 }
feef2cb5 580 break;
581
582 case MODE_PASSWORDBOX:
583 rc = inputBox(text, height, width, optCon, flags | FLAG_PASSWORD,
584 &result);
f40f18ae
ML
585 if (rc == DLG_OKAY) {
586 fprintf (output, "%s", result);
587 free(result);
588 }
6f481af2 589 break;
9be6d60e 590
591 case MODE_MENU:
9c4eb622 592 rc = listBox(text, height, width, listHeight, optCon, flags, default_item, &result);
f40f18ae
ML
593 if (rc == DLG_OKAY) {
594 fprintf(output, "%s", result);
595 free(result);
596 }
6f481af2 597 break;
9be6d60e 598
599 case MODE_RADIOLIST:
9c4eb622 600 rc = checkList(text, height, width, listHeight, optCon, 1, flags, &selections);
f40f18ae
ML
601 if (rc == DLG_OKAY && selections[0]) {
602 fprintf(output, "%s", selections[0]);
603 free(selections[0]);
6f481af2 604 free(selections);
605 }
606 break;
9be6d60e 607
608 case MODE_CHECKLIST:
9c4eb622 609 rc = checkList(text, height, width, listHeight, optCon, 0, flags, &selections);
6f481af2 610
611 if (!rc) {
612 for (next = selections; *next; next++) {
613 if (!separateOutput) {
614 if (needSpace) putc(' ', output);
615 fprintf(output, "\"%s\"", *next);
616 needSpace = 1;
617 } else {
618 fprintf(output, "%s\n", *next);
619 }
f40f18ae 620 free(*next);
6f481af2 621 }
622
623 free(selections);
624 }
625 break;
9be6d60e 626
627 case MODE_GAUGE:
6f481af2 628 rc = gauge(text, height, width, optCon, fd, flags);
629 break;
9be6d60e 630
631 default:
feef2cb5 632 usage(WAS_ERROR);
9be6d60e 633 }
634
feef2cb5 635 if (rc == DLG_ERROR) usage(WAS_ERROR);
649a0152 636
9be6d60e 637 if (clear)
6f481af2 638 newtPopWindow();
9be6d60e 639 newtFinished();
640
3341bdc5
ML
641 free(text);
642 poptFreeContext(optCon);
643
feef2cb5 644 return ( rc == DLG_ESCAPE ) ? -1 : rc;
9be6d60e 645}