Add support for "debconf directives", written ${!DIRECTIVE}
Directives are special variables written like ${!DIRECTIVE} which
expansion are delegated to the frontends, through the new
lookup_directive() "method".
As the frontend object is now given to question_get_field(), a new
function, question_get_raw_field() has been introduced to use in cdebconf
core.
Implementation details:
* lookup_vars() returns NULL when variables start with '!' and
strexpand() will skip the expansion in that case.
* question_get_field() makes another call to strexpand() after
having expanded variables in order to expand directives; giving it a
wrapper around the lookup_directive "method" as the lookup function.
* commands.c has been updated to use question_get_raw_* variants.
* text, newt and gtk frontends have been updated to give
question_get_field() proper arguments.
* question_get_text() expands directives as it is only used in
frontends.
* The q_get_choice_vals() macro uses question_get_raw_field() as
(multi)select results should not be processed by directive expansion.
r52209
Jérémy Bobbio
16 years ago
0 | debconf directives | |
1 | ================== | |
2 | ||
3 | cdebconf implements an extension to the Debconf specifications called | |
4 | directives. Directives are written in templates or substitutions enclosed into | |
5 | "${!" and "}", for example "${!DIRECTIVE}". | |
6 | ||
7 | . Directives are not expanded like normal variables and will be given back | |
8 | /!\ as is by GET and METAGET commands. This is the desired behaviour when, | |
9 | ~~~ for examples, one implements a dynamic Choices field for select questions | |
10 | with directives in SUBSTitutions. | |
11 | ||
12 | Directives are expanded according to the result of the lookup_directive() | |
13 | "method" implemented in the frontends when question_get_field() is called. | |
14 | By default, they will be expanded to the empty string, removing them from the | |
15 | returned value for unimplemented directives. |
301 | 301 | Currently, the only capability supported is DCF_CAPB_BACKUP, which |
302 | 302 | means the front end is capable of backing up, so return either |
303 | 303 | 0 of DCF_CAPB_BACKUP. |
304 | * const char * lookup_directive(struct frontend *, const char *) | |
305 | Return the proper value for the given directive. If NULL is returned, | |
306 | the directive is not expanded. | |
307 | The default method always returns "" to remove unhandled directives from | |
308 | queried question fields. | |
304 | 309 | * int add(struct frontend *, struct question *): |
305 | 310 | Add question. Default implementation adds the question to "questions" |
306 | 311 | attribute |
462 | 462 | return out; |
463 | 463 | } |
464 | 464 | |
465 | value = question_get_field(q, "", argv[1]); | |
465 | value = question_get_raw_field(q, "", argv[1]); | |
466 | 466 | if (value == NULL) |
467 | 467 | asprintf(&out, "%u %s does not exist", CMDSTATUS_BADQUESTION, argv[1]); |
468 | 468 | else |
651 | 651 | CMDSTATUS_BADQUESTION, argv[3]); |
652 | 652 | return out; |
653 | 653 | } |
654 | value = question_get_field(q, "", "description"); | |
654 | value = question_get_raw_field(q, "", "description"); | |
655 | 655 | question_deref(q); |
656 | 656 | if (value == NULL) |
657 | 657 | { |
695 | 695 | CMDSTATUS_BADQUESTION, argv[1]); |
696 | 696 | return out; |
697 | 697 | } |
698 | value = question_get_field(q, "", "description"); | |
698 | value = question_get_raw_field(q, "", "description"); | |
699 | 699 | question_deref(q); |
700 | 700 | if (value == NULL) |
701 | 701 | { |
744 | 744 | asprintf(&out, "%u %s does not exist", CMDSTATUS_BADQUESTION, arg); |
745 | 745 | return out; |
746 | 746 | } |
747 | value = question_get_field(q, "", "description"); | |
747 | value = question_get_raw_field(q, "", "description"); | |
748 | 748 | question_deref(q); |
749 | 749 | if (value == NULL) |
750 | 750 | { |
72 | 72 | return 0; |
73 | 73 | } |
74 | 74 | |
75 | static const char * frontend_lookup_directive(struct frontend *obj, | |
76 | const char *directive) | |
77 | { | |
78 | /* Remove unhandled directives. */ | |
79 | return ""; | |
80 | } | |
81 | ||
75 | 82 | static void frontend_set_title(struct frontend *f, const char *title) |
76 | 83 | { |
77 | 84 | DELETE(f->title); |
199 | 206 | SETMETHOD(initialize); |
200 | 207 | SETMETHOD(shutdown); |
201 | 208 | SETMETHOD(query_capability); |
209 | SETMETHOD(lookup_directive); | |
202 | 210 | SETMETHOD(set_title); |
203 | 211 | SETMETHOD(info); |
204 | 212 | SETMETHOD(add); |
7 | 7 | #include <stdbool.h> |
8 | 8 | |
9 | 9 | #include "constants.h" |
10 | #include "strutl.h" | |
10 | 11 | |
11 | 12 | #undef _ |
12 | 13 | #define _(x) (x) |
24 | 25 | int (*initialize)(struct frontend *, struct configuration *); |
25 | 26 | int (*shutdown)(struct frontend *); |
26 | 27 | unsigned long (*query_capability)(struct frontend *); |
28 | const char * (*lookup_directive)(struct frontend *, const char *); | |
27 | 29 | void (*set_title)(struct frontend *, const char *title); |
28 | 30 | void (*info)(struct frontend *, struct question *); |
29 | 31 | int (*add)(struct frontend *, struct question *q); |
87 | 87 | struct question * question = fe->qdb->methods.get(fe->qdb, template); |
88 | 88 | char * text; |
89 | 89 | |
90 | text = question ? q_get_description(question) : g_strdup(fallback); | |
90 | text = question ? q_get_description(fe, question) : g_strdup(fallback); | |
91 | 91 | question_deref(question); |
92 | 92 | return text; |
93 | 93 | } |
140 | 140 | return NULL; |
141 | 141 | }; |
142 | 142 | |
143 | raw_indices = q_get_indices(question); | |
144 | raw_choices = q_get_choices_vals(question); | |
145 | raw_translated_choices = q_get_choices(question); | |
143 | raw_indices = q_get_indices(fe, question); | |
144 | raw_choices = q_get_choices_vals(fe, question); | |
145 | raw_translated_choices = q_get_choices(fe, question); | |
146 | 146 | count = strgetargc(raw_choices); |
147 | 147 | g_assert(0 < count); |
148 | 148 | /* check NULLs! */ |
139 | 139 | GtkTextIter end; |
140 | 140 | char * description; |
141 | 141 | |
142 | description = q_get_description(question); | |
142 | description = q_get_description(fe, question); | |
143 | 143 | /* XXX: check NULL! */ |
144 | 144 | view = gtk_text_view_new(); |
145 | 145 | /* XXX: check NULL! */ |
184 | 184 | /* here is created the question's extended description, but only |
185 | 185 | * if the question's extended description actually exists |
186 | 186 | */ |
187 | ext_description = q_get_extended_description(question); | |
187 | ext_description = q_get_extended_description(fe, question); | |
188 | 188 | if ('\0' != ext_description[0]) { |
189 | 189 | view = gtk_text_view_new(); |
190 | 190 | buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view)); |
264 | 264 | hide_expanders(GTK_TREE_VIEW(view)); |
265 | 265 | } |
266 | 266 | |
267 | description = q_get_description(question); | |
267 | description = q_get_description(fe, question); | |
268 | 268 | text_renderer = gtk_cell_renderer_text_new(); |
269 | 269 | gtk_tree_view_insert_column_with_attributes( |
270 | 270 | GTK_TREE_VIEW(view), -1 /* insert at the end */, |
103 | 103 | char * text; |
104 | 104 | |
105 | 105 | if (NULL != fe->info) { |
106 | text = q_get_description(fe->info); | |
106 | text = q_get_description(fe, fe->info); | |
107 | 107 | message = g_strdup_printf( |
108 | 108 | "<b><span foreground=\"#ffffff\">%s</span></b>", text); |
109 | 109 | layout = gtk_widget_create_pango_layout( |
244 | 244 | } |
245 | 245 | |
246 | 246 | static int |
247 | min_window_height(struct question *q, int win_width) | |
247 | min_window_height(struct frontend *obj, struct question *q, int win_width) | |
248 | 248 | { |
249 | 249 | int height = 3; |
250 | 250 | char *type = q->template->type; |
251 | 251 | char *q_ext_text; |
252 | 252 | |
253 | q_ext_text = q_get_extended_description(q); | |
253 | q_ext_text = q_get_extended_description(obj, q); | |
254 | 254 | if (q_ext_text != NULL) |
255 | 255 | height = cdebconf_newt_get_text_height(q_ext_text, win_width) + 1; |
256 | 256 | if (strcmp(type, "multiselect") == 0 || strcmp(type, "select") == 0) |
262 | 262 | } |
263 | 263 | |
264 | 264 | static int |
265 | need_separate_window(struct question *q) | |
265 | need_separate_window(struct frontend *obj, struct question *q) | |
266 | 266 | { |
267 | 267 | int width = 80, height = 24; |
268 | 268 | int x; |
269 | 269 | |
270 | 270 | newtGetScreenSize(&width, &height); |
271 | x = min_window_height(q, width-7); | |
271 | x = min_window_height(obj, q, width-7); | |
272 | 272 | return (x >= height-5); |
273 | 273 | } |
274 | 274 | |
275 | 275 | static char * |
276 | get_full_description(struct question *q) | |
276 | get_full_description(struct frontend *obj, struct question *q) | |
277 | 277 | { |
278 | 278 | char *res = NULL; |
279 | char *descr = q_get_description(q); | |
280 | char *ext_descr = q_get_extended_description(q); | |
279 | char *descr = q_get_description(obj, q); | |
280 | char *ext_descr = q_get_extended_description(obj, q); | |
281 | 281 | |
282 | 282 | assert(descr); |
283 | 283 | assert(ext_descr); |
311 | 311 | #else |
312 | 312 | int flags = NEWT_FLAG_WRAP; |
313 | 313 | #endif |
314 | char *descr = q_get_description(q); | |
315 | char *ext_descr = q_get_extended_description(q); | |
314 | char *descr = q_get_description(obj, q); | |
315 | char *ext_descr = q_get_extended_description(obj, q); | |
316 | 316 | |
317 | 317 | assert(descr); |
318 | 318 | assert(ext_descr); |
324 | 324 | full_description = strdup(ext_descr); |
325 | 325 | } |
326 | 326 | else |
327 | full_description = get_full_description(q); | |
327 | full_description = get_full_description(obj, q); | |
328 | 328 | |
329 | 329 | newtGetScreenSize(&width, &height); |
330 | 330 | win_width = width-7; |
409 | 409 | #endif |
410 | 410 | char *defval; |
411 | 411 | const char *result; |
412 | char *full_description = get_full_description(q); | |
412 | char *full_description = get_full_description(obj, q); | |
413 | 413 | |
414 | 414 | eflags |= NEWT_ENTRY_SCROLL | NEWT_FLAG_RETURNEXIT; |
415 | 415 | newtGetScreenSize(&width, &height); |
495 | 495 | size_t res; |
496 | 496 | wchar_t c; |
497 | 497 | int *tindex = NULL; |
498 | const char *indices = q_get_indices(q); | |
499 | char *full_description = get_full_description(q); | |
498 | const char *indices = q_get_indices(obj, q); | |
499 | char *full_description = get_full_description(obj, q); | |
500 | 500 | #ifdef HAVE_LIBTEXTWRAP |
501 | 501 | textwrap_t tw; |
502 | 502 | char *wrappedtext; |
508 | 508 | newtGetScreenSize(&width, &height); |
509 | 509 | win_width = width-7; |
510 | 510 | strtruncate(obj->title, win_width-9); |
511 | count = strgetargc(q_get_choices_vals(q)); | |
511 | count = strgetargc(q_get_choices_vals(obj, q)); | |
512 | 512 | if (count <= 0) |
513 | 513 | return DC_NOTOK; |
514 | 514 | choices = malloc(sizeof(char *) * count); |
515 | 515 | choices_trans = malloc(sizeof(char *) * count); |
516 | 516 | tindex = malloc(sizeof(int) * count); |
517 | if (strchoicesplitsort(q_get_choices_vals(q), q_get_choices(q), indices, choices, choices_trans, tindex, count) != count) | |
517 | if (strchoicesplitsort(q_get_choices_vals(obj, q), q_get_choices(obj, q), indices, choices, choices_trans, tindex, count) != count) | |
518 | 518 | return DC_NOTOK; |
519 | 519 | |
520 | 520 | defvals = malloc(sizeof(char *) * count); |
647 | 647 | char **choices, **choices_trans, *defval; |
648 | 648 | int count = 0, i, ret, defchoice = -1; |
649 | 649 | int *tindex = NULL; |
650 | const char *indices = q_get_indices(q); | |
651 | char *full_description = get_full_description(q); | |
650 | const char *indices = q_get_indices(obj, q); | |
651 | char *full_description = get_full_description(obj, q); | |
652 | 652 | char *p; |
653 | 653 | size_t res; |
654 | 654 | int k; |
664 | 664 | newtGetScreenSize(&width, &height); |
665 | 665 | win_width = width-7; |
666 | 666 | strtruncate(obj->title, win_width-9); |
667 | count = strgetargc(q_get_choices_vals(q)); | |
667 | count = strgetargc(q_get_choices_vals(obj, q)); | |
668 | 668 | if (count <= 0) |
669 | 669 | return DC_NOTOK; |
670 | 670 | choices = malloc(sizeof(char *) * count); |
671 | 671 | choices_trans = malloc(sizeof(char *) * count); |
672 | 672 | tindex = malloc(sizeof(int) * count); |
673 | if (strchoicesplitsort(q_get_choices_vals(q), q_get_choices(q), indices, choices, choices_trans, tindex, count) != count) | |
673 | if (strchoicesplitsort(q_get_choices_vals(obj, q), q_get_choices(obj, q), indices, choices, choices_trans, tindex, count) != count) | |
674 | 674 | return DC_NOTOK; |
675 | 675 | |
676 | 676 | sel_height = count; |
787 | 787 | #else |
788 | 788 | int flags = NEWT_FLAG_WRAP; |
789 | 789 | #endif |
790 | char *full_description = get_full_description(q); | |
790 | char *full_description = get_full_description(obj, q); | |
791 | 791 | |
792 | 792 | newtGetScreenSize(&width, &height); |
793 | 793 | win_width = width-7; |
877 | 877 | { |
878 | 878 | int separate_window, ret; |
879 | 879 | |
880 | separate_window = need_separate_window(q); | |
880 | separate_window = need_separate_window(obj, q); | |
881 | 881 | while (1) { |
882 | 882 | if (separate_window) { |
883 | 883 | ret = show_separate_window(obj, q); |
905 | 905 | { |
906 | 906 | int separate_window, ret; |
907 | 907 | |
908 | separate_window = need_separate_window(q); | |
908 | separate_window = need_separate_window(obj, q); | |
909 | 909 | while (1) { |
910 | 910 | if (separate_window) { |
911 | 911 | ret = show_separate_window(obj, q); |
1094 | 1094 | newtCls(); |
1095 | 1095 | } |
1096 | 1096 | if (obj->info != NULL) { |
1097 | char *text = q_get_description(obj->info); | |
1097 | char *text = q_get_description(obj, obj->info); | |
1098 | 1098 | if (text) |
1099 | 1099 | newtDrawRootText(0, 0, text); |
1100 | 1100 | free(text); |
1235 | 1235 | newtSetColors(newtAltColorPalette); |
1236 | 1236 | newtCls(); |
1237 | 1237 | if (obj->info != NULL) { |
1238 | char *text = q_get_description(obj->info); | |
1238 | char *text = q_get_description(obj, obj->info); | |
1239 | 1239 | if (text) |
1240 | 1240 | newtDrawRootText(0, 0, text); |
1241 | 1241 | free(text); |
160 | 160 | */ |
161 | 161 | static void text_handler_displaydesc(struct frontend *obj, struct question *q) |
162 | 162 | { |
163 | char *descr = q_get_description(q); | |
164 | char *ext_descr = q_get_extended_description(q); | |
163 | char *descr = q_get_description(obj, q); | |
164 | char *ext_descr = q_get_extended_description(obj, q); | |
165 | 165 | if (strcmp(q->template->type, "note") == 0 || |
166 | 166 | strcmp(q->template->type, "error") == 0) |
167 | 167 | { |
193 | 193 | static void |
194 | 194 | show_help (struct frontend *obj, struct question *q) |
195 | 195 | { |
196 | char *descr = q_get_description(q); | |
196 | char *descr = q_get_description(obj, q); | |
197 | 197 | printf("%s", question_get_text(obj, "debconf/text-help-keystrokes", "KEYSTROKES:")); |
198 | 198 | printf("\n %c ", CHAR_HELP); |
199 | 199 | printf("%s", question_get_text(obj, "debconf/text-help-help", "Display this help message")); |
387 | 387 | int i, j, count = 0, dcount, choice; |
388 | 388 | int *tindex = NULL; |
389 | 389 | int ret = DC_OK; |
390 | const char *indices = q_get_indices(q); | |
391 | ||
392 | count = strgetargc(q_get_choices_vals(q)); | |
390 | const char *indices = q_get_indices(obj, q); | |
391 | ||
392 | count = strgetargc(q_get_choices_vals(obj, q)); | |
393 | 393 | if (count <= 0) |
394 | 394 | return DC_NOTOK; |
395 | 395 | choices = malloc(sizeof(char *) * count); |
399 | 399 | choices[i] = choices_translated[i] = NULL; |
400 | 400 | } |
401 | 401 | tindex = malloc(sizeof(int) * count); |
402 | if (strchoicesplitsort(q_get_choices_vals(q), q_get_choices(q), indices, choices, choices_translated, tindex, count) != count) | |
402 | if (strchoicesplitsort(q_get_choices_vals(obj, q), q_get_choices(obj, q), indices, choices, choices_translated, tindex, count) != count) | |
403 | 403 | { |
404 | 404 | ret = DC_NOTOK; |
405 | 405 | goto CleanUp_TINDEX; |
514 | 514 | const char *defval = question_getvalue(q, ""); |
515 | 515 | int *tindex = NULL; |
516 | 516 | int ret = DC_OK; |
517 | const char *indices = q_get_indices(q); | |
518 | ||
519 | count = strgetargc(q_get_choices_vals(q)); | |
517 | const char *indices = q_get_indices(obj, q); | |
518 | ||
519 | count = strgetargc(q_get_choices_vals(obj, q)); | |
520 | 520 | if (count <= 0) |
521 | 521 | return DC_NOTOK; |
522 | 522 | choices = malloc(sizeof(char *) * count); |
526 | 526 | choices[i] = choices_translated[i] = NULL; |
527 | 527 | } |
528 | 528 | tindex = malloc(sizeof(int) * count); |
529 | if (strchoicesplitsort(q_get_choices_vals(q), q_get_choices(q), indices, choices, choices_translated, tindex, count) != count) | |
529 | if (strchoicesplitsort(q_get_choices_vals(obj, q), q_get_choices(obj, q), indices, choices, choices_translated, tindex, count) != count) | |
530 | 530 | { |
531 | 531 | ret = DC_NOTOK; |
532 | 532 | goto CleanUp_TINDEX; |
172 | 172 | { |
173 | 173 | struct questionvariable * current; |
174 | 174 | |
175 | /* Skip directives */ | |
176 | if ('!' == name[0]) { | |
177 | return NULL; | |
178 | } | |
175 | 179 | for (current = variables; NULL != current; current = current->next) { |
176 | 180 | if (0 == strcmp(current->variable, name)) { |
177 | 181 | return current->value; |
185 | 189 | return strexpand(field, LOOKUP_FUNCTION(lookup_vars), q->variables); |
186 | 190 | } |
187 | 191 | |
188 | char *question_get_field(const struct question *q, const char *lang, | |
192 | char *question_get_raw_field(const struct question *q, const char *lang, | |
189 | 193 | const char *field) |
190 | 194 | { |
191 | 195 | char *ret; |
203 | 207 | return strdup(""); |
204 | 208 | else |
205 | 209 | return ret; |
210 | } | |
211 | ||
212 | static const char * lookup_directive(const char * directive, struct frontend * fe) | |
213 | { | |
214 | assert('!' == directive[0]); | |
215 | return fe->methods.lookup_directive(fe, directive + 1 /* drop '!' */); | |
216 | } | |
217 | ||
218 | char * question_get_field(struct frontend * fe, const struct question * q, | |
219 | const char * lang, const char * field) | |
220 | { | |
221 | char * raw; | |
222 | char * ret; | |
223 | ||
224 | raw = question_get_raw_field(q, lang, field); | |
225 | ret = strexpand(raw, LOOKUP_FUNCTION(lookup_directive), fe); | |
226 | free(raw); | |
227 | return ret; | |
206 | 228 | } |
207 | 229 | |
208 | 230 | /* |
220 | 242 | { |
221 | 243 | struct question *q = obj->qdb->methods.get(obj->qdb, template); |
222 | 244 | const char *text; |
223 | text = (q ? q_get_description(q) : fallback); | |
245 | text = (q ? q_get_description(obj, q) : fallback); | |
224 | 246 | question_deref(q); |
225 | 247 | return text; |
226 | 248 | } |
7 | 7 | #define DC_QFLAG_SEEN (1 << 0) |
8 | 8 | #define DC_QFLAG_DONTPARSE (1 << 1) |
9 | 9 | |
10 | #define q_get_extended_description(q) question_get_field((q), "", "extended_description") | |
11 | #define q_get_description(q) question_get_field((q), "", "description") | |
12 | #define q_get_choices(q) question_get_field((q), "", "choices") | |
13 | #define q_get_choices_vals(q) question_get_field((q), "C", "choices") | |
14 | #define q_get_indices(q) question_get_field((q), "", "indices") | |
10 | #define q_get_extended_description(fe, q) question_get_field((fe), (q), "", "extended_description") | |
11 | #define q_get_description(fe, q) question_get_field((fe), (q), "", "description") | |
12 | #define q_get_choices(fe, q) question_get_field((fe), (q), "", "choices") | |
13 | #define q_get_choices_vals(fe, q) question_get_raw_field((q), "C", "choices") | |
14 | #define q_get_indices(fe, q) question_get_field((fe), (q), "", "indices") | |
15 | #define q_get_raw_extended_description(q) question_get_raw_field((q), "", "extended_description") | |
16 | #define q_get_raw_description(q) question_get_raw_field((q), "", "description") | |
17 | #define q_get_raw_choices(q) question_get_raw_field((q), "", "choices") | |
18 | #define q_get_raw_choices_vals(q) question_get_raw_field((q), "C", "choices") | |
19 | #define q_get_raw_indices(q) question_get_raw_field((q), "", "indices") | |
15 | 20 | |
16 | 21 | struct template; |
17 | 22 | struct frontend; |
66 | 71 | |
67 | 72 | void question_owner_add(struct question *q, const char *owner); |
68 | 73 | void question_owner_delete(struct question *q, const char *owner); |
69 | char *question_get_field(const struct question *q, const char *lang, | |
74 | char *question_get_raw_field(const struct question *q, const char *lang, | |
70 | 75 | const char *field); |
76 | char *question_get_field(struct frontend *obj, const struct question *q, | |
77 | const char *lang, const char *field); | |
71 | 78 | |
72 | 79 | const char *question_get_text(struct frontend *obj, const char *template, |
73 | 80 | const char *fallback); |
607 | 607 | var[j] = '\0'; |
608 | 608 | dest_size += rope[rope_index++].len; |
609 | 609 | rope[rope_index].str = func(var, user_data); |
610 | rope[rope_index].len = strlen(rope[rope_index].str); | |
610 | /* Handle skips */ | |
611 | if (NULL == rope[rope_index].str) { | |
612 | rope[rope_index].str = &src[i - j - 2 /* "${" */]; | |
613 | rope[rope_index].len = j + 3; /* "${}" */ | |
614 | } else { | |
615 | rope[rope_index].len = strlen(rope[rope_index].str); | |
616 | } | |
611 | 617 | dest_size += rope[rope_index++].len; |
612 | 618 | /* skip '}' */ |
613 | 619 | rope[rope_index].str = &src[i + 1]; |