run_match.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. /* $Id: run_match.c,v 1.25 2001/05/06 01:23:05 kilobug Exp $ */
  2. #include "mclient.h"
  3. #define ALLOWED_CHARS "_+-$&*!@#~.éáàèëïûôêîìä"
  4. typedef struct _player_t
  5. {
  6. int id;
  7. float score;
  8. GdkColor *color;
  9. } player_t;
  10. typedef struct _match_t
  11. {
  12. GtkWidget *gfx, *port, *cmd, *public, *turns;
  13. int mapid;
  14. player_t *players;
  15. GtkWidget **wids;
  16. } match_t;
  17. typedef struct _rmatch_t
  18. {
  19. GtkWidget *dialog, *textarea;
  20. int fd;
  21. int pid, mysql_id;
  22. player_t *players;
  23. int nbp;
  24. int gdki;
  25. char *buf;
  26. } rmatch_t;
  27. static void kill_match(GtkWidget *wi, rmatch_t*rm)
  28. {
  29. if (rm->pid > 0)
  30. kill(rm->pid, SIGUSR2);
  31. }
  32. static gboolean match_display_line(rmatch_t *rm, const char *buf,
  33. gboolean force);
  34. static void match_fetch_player(player_t *players, int num, int nbp)
  35. {
  36. int i, class = 1;
  37. if (gl_config->staff && (num > 0) && (nbp > 1))
  38. {
  39. for (i = 1; i <= nbp; i++)
  40. if (players[i].score > players[num].score)
  41. class *= 2;
  42. i = nbp * 100 / class;
  43. mysql_do_query(gl_config->mysql,
  44. "UPDATE Champions "
  45. "SET nbmap=nbmap+1, total=total+%d "
  46. "WHERE id=%d",
  47. i, players[num].id);
  48. mysql_do_query(gl_config->mysql,
  49. "UPDATE Champions "
  50. "SET score=total/nbmap, best=GREATEST(best, %f) "
  51. "WHERE id=%d", players[num].score, players[num].id);
  52. }
  53. g_free(players[num].color);
  54. }
  55. static void match_end(GtkWidget *wi, rmatch_t*rm)
  56. {
  57. int i;
  58. gdk_input_remove(rm->gdki);
  59. match_display_line(rm, "<server> Match terminé.\n", FALSE);
  60. match_display_line(rm, "\n", TRUE);
  61. close(rm->fd);
  62. g_free(rm->buf);
  63. for (i = 0; i <= rm->nbp; i++)
  64. match_fetch_player(rm->players, i, rm->nbp);
  65. g_free(rm->players);
  66. }
  67. static void kill_match_window(GtkWidget *wi, rmatch_t *rm)
  68. {
  69. gtk_widget_destroy(rm->dialog);
  70. rm->dialog = NULL;
  71. rm->textarea = NULL;
  72. }
  73. static void match_check_score(rmatch_t *rm, const char *buf)
  74. {
  75. int i;
  76. float f;
  77. char *s;
  78. if (strncmp(buf, "<server> Player ", 16))
  79. return;
  80. i = strtol(buf + 16, &s, 10);
  81. s += 3;
  82. f = atof(s);
  83. mysql_do_query(gl_config->mysql,
  84. "INSERT INTO Scores SET champion=%d, score='%f', match_id=%d",
  85. rm->players[i].id, f, rm->mysql_id);
  86. rm->players[i].score = f;
  87. }
  88. static void match_check_bouvier(rmatch_t *rm, const char *buf)
  89. {
  90. const char *p;
  91. char *q;
  92. int i;
  93. char b[MYSQL_BUFFER_SIZE];
  94. p = strchr(buf, '(');
  95. if (p == NULL)
  96. return;
  97. p++;
  98. i = strtol(p, &q, 10);
  99. if (strcmp(q, ")> Bravo, tu as gagne un tour de patio!\n"))
  100. return;
  101. q = mysql_get_str(gl_config->mysql,
  102. "SELECT b.login FROM Bitmaps b, Champions c "
  103. "WHERE c.id=%d AND c.auteur = b.id", rm->players[i].id);
  104. snprintf(b, MYSQL_BUFFER_SIZE,
  105. "ns_send_msg %s 'Bravo, tu as gagne un tour de patio!'",
  106. q);
  107. system(b);
  108. snprintf(b, MYSQL_BUFFER_SIZE,
  109. "echo '%s a gagne un tour de patio!' | mail bouvie_p -s Bouvieur "
  110. "-c prolo",
  111. q);
  112. system(b);
  113. g_free(q);
  114. }
  115. static gboolean match_display_line(rmatch_t *rm, const char *buf,
  116. gboolean force)
  117. {
  118. char *p;
  119. int id;
  120. static int last_id = -42;
  121. static char *buf2 = NULL;
  122. if (!strcmp(buf, "Bye!\n"))
  123. {
  124. match_end(NULL, rm);
  125. return FALSE;
  126. }
  127. match_check_score(rm, buf);
  128. match_check_bouvier(rm, buf);
  129. if (rm->textarea == NULL)
  130. return TRUE;
  131. p = strchr(buf, '(');
  132. if (p == NULL)
  133. id = 0;
  134. else
  135. id = atoi(p + 1);
  136. if (id > rm->nbp)
  137. id = 0;
  138. if (id == 0)
  139. force = TRUE;
  140. if ((id == last_id) && (!force))
  141. {
  142. p = buf2;
  143. buf2 = g_strconcat(p, buf, NULL);
  144. g_free(p);
  145. }
  146. else
  147. {
  148. if (buf2 != NULL)
  149. {
  150. gtk_text_insert(GTK_TEXT(rm->textarea), NULL,
  151. rm->players[last_id].color,
  152. NULL, buf2, -1);
  153. g_free(buf2);
  154. }
  155. buf2 = g_strdup(buf);
  156. last_id = id;
  157. }
  158. return TRUE;
  159. }
  160. static void match_get_data(rmatch_t *rm, int fd, GdkInputCondition foo)
  161. {
  162. char buf[MYSQL_BUFFER_SIZE], *s, *p;
  163. int sz, l;
  164. gboolean r;
  165. GtkAdjustment *adj;
  166. if (rm == NULL)
  167. return;
  168. sz = read(fd, buf, MYSQL_BUFFER_SIZE - 1);
  169. if (sz == -1)
  170. {
  171. match_end(NULL, rm);
  172. return;
  173. }
  174. buf[sz] = 0;
  175. s = g_strconcat(rm->buf, buf, NULL);
  176. g_free(rm->buf);
  177. rm->buf = s;
  178. if (rm->textarea != NULL)
  179. {
  180. gtk_text_freeze(GTK_TEXT(rm->textarea));
  181. l = gtk_text_get_length(GTK_TEXT(rm->textarea));
  182. l -= 5000;
  183. if (l > 0)
  184. {
  185. while ((GTK_TEXT_INDEX(GTK_TEXT(rm->textarea), l) != '\n') && l > 0)
  186. l--;
  187. if (l > 0)
  188. {
  189. gtk_text_set_point(GTK_TEXT(rm->textarea), 0);
  190. gtk_text_forward_delete(GTK_TEXT(rm->textarea), l);
  191. gtk_text_set_point(GTK_TEXT(rm->textarea),
  192. gtk_text_get_length(GTK_TEXT(rm->textarea)));
  193. }
  194. }
  195. }
  196. while ((p = strchr(rm->buf, '\n')) != NULL)
  197. {
  198. s = rm->buf;
  199. rm->buf = g_strdup(p + 1);
  200. p[1] = 0;
  201. r = match_display_line(rm, s, FALSE);
  202. g_free(s);
  203. if (!r)
  204. break;
  205. }
  206. if (rm->textarea != NULL)
  207. {
  208. gtk_text_thaw(GTK_TEXT(rm->textarea));
  209. adj = GTK_TEXT(rm->textarea)->vadj;
  210. gtk_adjustment_set_value(adj, adj->upper);
  211. }
  212. }
  213. static int my_creneau(int val)
  214. {
  215. if (val >= 0)
  216. return CLAMP((120 - val) * 0xFF / 120, 0, 0xFF);
  217. else
  218. return CLAMP((120 + val) * 0xFF / 120, 0, 0xFF);
  219. }
  220. static GdkColor *create_player_color(int i, int nb)
  221. {
  222. int angle;
  223. GdkColor *col;
  224. col = g_malloc(sizeof(*col));
  225. col->pixel = 0;
  226. angle = (360 * i) / nb; /* position => angle dans spectre */
  227. if (angle > 180)
  228. angle = angle - 360;
  229. col->red = my_creneau(angle) * 128;
  230. col->green = my_creneau(angle - 120) * 128;
  231. col->blue = my_creneau(angle + 120) * 128;
  232. gdk_colormap_alloc_color(gdk_colormap_get_system(), col, TRUE, TRUE);
  233. return col;
  234. }
  235. static void create_listener(int fd, int pid, int nbp, int mysql_id,
  236. int *play_ids)
  237. {
  238. rmatch_t *rm;
  239. GtkWidget *sw, *dial, *widget, *hbox;
  240. GtkScrolledWindow *scrw;
  241. int i;
  242. rm = g_malloc(sizeof(*rm));
  243. rm->fd = fd;
  244. rm->gdki = gdk_input_add(fd, GDK_INPUT_READ,
  245. (GdkInputFunction) (match_get_data), rm);
  246. rm->pid = pid;
  247. rm->mysql_id = mysql_id;
  248. rm->nbp = nbp;
  249. rm->buf = g_strdup("");
  250. rm->players = g_malloc(sizeof(*(rm->players)) * (rm->nbp + 1));
  251. for (i = 0; i <= rm->nbp; i++)
  252. {
  253. rm->players[i].id = (i > 0) ? play_ids[i - 1] : 0;
  254. rm->players[i].score = 0;
  255. rm->players[i].color = create_player_color(i, rm->nbp + 1);
  256. }
  257. /* Main box */
  258. dial = rm->dialog = gtk_dialog_new();
  259. gtk_widget_hide(dial);
  260. gtk_signal_connect(GTK_OBJECT(dial), "destroy",
  261. GTK_SIGNAL_FUNC(kill_match_window), rm);
  262. gtk_window_set_title(GTK_WINDOW(dial), "Suivi d'un match");
  263. gtk_container_set_border_width(GTK_CONTAINER(&(GTK_DIALOG(dial)->window)),
  264. 7);
  265. sw = gtk_scrolled_window_new(NULL, NULL);
  266. scrw = GTK_SCROLLED_WINDOW(sw);
  267. rm->textarea = gtk_text_new(gtk_scrolled_window_get_hadjustment(scrw),
  268. gtk_scrolled_window_get_vadjustment(scrw));
  269. gtk_text_set_word_wrap(GTK_TEXT(rm->textarea), FALSE);
  270. gtk_text_set_line_wrap(GTK_TEXT(rm->textarea), TRUE);
  271. gtk_text_set_editable(GTK_TEXT(rm->textarea), FALSE);
  272. gtk_widget_show(sw);
  273. gtk_scrolled_window_set_policy(scrw, GTK_POLICY_AUTOMATIC,
  274. GTK_POLICY_AUTOMATIC);
  275. gtk_container_add(GTK_CONTAINER(sw), rm->textarea);
  276. gtk_widget_show(rm->textarea);
  277. /* Buttons */
  278. hbox = gtk_hbox_new(FALSE, 3);
  279. gtk_widget_show(hbox);
  280. widget = gtk_button_new_with_label(" Arrêter ");
  281. gtk_widget_show(widget);
  282. gtk_signal_connect(GTK_OBJECT(widget), "clicked",
  283. GTK_SIGNAL_FUNC(kill_match), rm);
  284. gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 9);
  285. widget = gtk_button_new_with_label(" Fermer ");
  286. gtk_widget_show(widget);
  287. gtk_signal_connect(GTK_OBJECT(widget), "clicked",
  288. GTK_SIGNAL_FUNC(kill_match_window), rm);
  289. gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 9);
  290. gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dial)->action_area), hbox, TRUE,
  291. TRUE, 9);
  292. gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dial)->vbox), sw, TRUE,
  293. TRUE, 9);
  294. gtk_window_set_default_size(GTK_WINDOW(&(GTK_DIALOG(dial)->window)),
  295. 340, 280);
  296. gtk_widget_show(rm->dialog);
  297. }
  298. static void do_run_match(const char *cmd, gboolean gfx, int portn, int id,
  299. int nbp, int *play_ids)
  300. {
  301. char port[11];
  302. char buf[MYSQL_BUFFER_SIZE];
  303. int fd[2];
  304. pid_t pid = -1;
  305. g_snprintf(port, 11, "%d", portn);
  306. pipe(fd);
  307. if (!fork())
  308. {
  309. close(1);
  310. dup2(fd[1], 1);
  311. close(fd[0]);
  312. if (gl_config->host == NULL)
  313. g_snprintf(buf, MYSQL_BUFFER_SIZE, "exec %s -p %s %s",
  314. gl_config->server, port, cmd);
  315. else
  316. g_snprintf(buf, MYSQL_BUFFER_SIZE, "exec ssh %s %s -p %s %s",
  317. gl_config->host, gl_config->server, port, cmd);
  318. system(buf);
  319. g_snprintf(buf, MYSQL_BUFFER_SIZE,
  320. "UPDATE Matchs SET statut=0 WHERE id=%d", id);
  321. mysql_query(gl_config->mysql, buf);
  322. _exit(1);
  323. }
  324. close(fd[1]);
  325. if (gfx)
  326. if (!(pid = fork()))
  327. {
  328. sleep(1);
  329. if (gl_config->host == NULL)
  330. execlp(gl_config->gfx_client, gl_config->gfx_client,
  331. "-d", "-p", port, NULL);
  332. else
  333. execlp(gl_config->gfx_client, gl_config->gfx_client,
  334. "-d", "-p", port, "-h", gl_config->host, NULL);
  335. _exit(1);
  336. }
  337. fcntl(fd[0], F_SETFL, O_NONBLOCK);
  338. create_listener(fd[0], pid, nbp, id, play_ids);
  339. }
  340. static void run_match_commit(GtkWidget *wid, match_t *match)
  341. {
  342. gboolean gfx, pub;
  343. int port;
  344. char buf[MYSQL_BUFFER_SIZE], buf2[MYSQL_BUFFER_SIZE];
  345. char *p = NULL, *s = NULL, *s2 = NULL;
  346. int i, j, nbp, play;
  347. int nbturn;
  348. int players_id[64];
  349. int id;
  350. if (match == NULL)
  351. return;
  352. if (match->wids == NULL)
  353. return;
  354. set_data_from_toggle(match->gfx, &gfx);
  355. set_data_from_spin(match->port, &port);
  356. set_data_from_spin(match->turns, &nbturn);
  357. if (gfx)
  358. set_data_from_toggle(match->public, &pub);
  359. else
  360. pub = FALSE;
  361. p = buf;
  362. s = mysql_get_str(gl_config->mysql,
  363. "SELECT fichier FROM Maps WHERE id=%d", match->mapid);
  364. if (!g_path_is_absolute(s) && getenv("PROLO_DATA_DIR") != NULL)
  365. {
  366. s2 = s;
  367. s = g_strconcat(getenv("PROLO_DATA_DIR"), G_DIR_SEPARATOR_S, s2, NULL);
  368. g_free(s2);
  369. }
  370. p += g_snprintf(buf, MYSQL_BUFFER_SIZE, "%s -m %s -n %d ",
  371. gfx ? "-g" : "", s, nbturn);
  372. g_free(s);
  373. for (play = i = 0; match->wids[i] != NULL; i++)
  374. {
  375. s2 = g_strdup(gtk_entry_get_text(GTK_ENTRY(match->wids[i])));
  376. if (strcmp(s2, "<Aucun>"))
  377. {
  378. players_id[play] = mysql_get_int(gl_config->mysql,
  379. "SELECT id "
  380. "FROM Champions WHERE Nom='%s'",
  381. s2);
  382. s = mysql_get_str(gl_config->mysql,
  383. "SELECT fichier FROM Champions WHERE id='%d'",
  384. players_id[play]);
  385. play++;
  386. for (j = 0; s2[j]; j++)
  387. if (!isalnum(s2[j]) && (strchr(ALLOWED_CHARS, s2[j]) == NULL))
  388. s2[j] = '_';
  389. p += g_snprintf(p, MYSQL_BUFFER_SIZE - (p - buf), "%s:%s ", s2, s);
  390. g_free(s);
  391. }
  392. g_free(s2);
  393. }
  394. nbp = play;
  395. g_snprintf(buf2, MYSQL_BUFFER_SIZE,
  396. "INSERT INTO Matchs SET map=%d, host='%s', port=%d, statut=1,"
  397. " joueurs=%d, auteur=%d, public=%d",
  398. match->mapid,
  399. (gl_config->host == NULL) ? gl_config->hostname : gl_config->host,
  400. port, nbp, getuid(), pub);
  401. mysql_query_gtk(gl_config->mysql, buf2);
  402. id = mysql_insert_id(gl_config->mysql);
  403. s = gtk_entry_get_text(GTK_ENTRY(match->cmd));
  404. p += g_snprintf(p, MYSQL_BUFFER_SIZE - (p - buf), "%s ", s);
  405. if (!pub)
  406. p += g_snprintf(p, MYSQL_BUFFER_SIZE - (p - buf), "-P ");
  407. g_free(match->wids);
  408. g_free(match);
  409. do_run_match(buf, gfx, port, id, nbp, players_id);
  410. }
  411. GtkWidget *start_match_box()
  412. {
  413. match_t *mat;
  414. GtkWidget *dialog = NULL, *tbl = NULL, *wid = NULL;
  415. GList *l, *champ = NULL, *maps, *prgs;
  416. char buf[MYSQL_BUFFER_SIZE];
  417. int nbp, nbc;
  418. int i, id;
  419. maps = GTK_CLIST(gl_config->mapl)->selection;
  420. if (maps == NULL)
  421. {
  422. wid = create_label("Aucune carte sélectionnée.");
  423. return create_dialog("Erreur", wid, NULL, " Fermer ", NULL, NULL,
  424. destroy_widget);
  425. }
  426. prgs = GTK_CLIST(gl_config->prgl)->selection;
  427. if (prgs == NULL)
  428. {
  429. wid = create_label("Aucun champion sélectionné.");
  430. return create_dialog("Erreur", wid, NULL, " Fermer ", NULL, NULL,
  431. destroy_widget);
  432. }
  433. mat = g_malloc(sizeof(*mat));
  434. for (l = prgs; l != NULL; l = l->next)
  435. {
  436. id = *((int *) gtk_clist_get_row_data(GTK_CLIST(gl_config->prgl),
  437. GPOINTER_TO_INT(l->data)));
  438. champ = g_list_append(champ, mysql_get_str(gl_config->mysql,
  439. "SELECT nom FROM Champions "
  440. "WHERE id=%d", id));
  441. }
  442. champ = g_list_append(champ, g_strdup("<Aucun>"));
  443. nbc = g_list_length(prgs);
  444. mat->mapid = *((int *) gtk_clist_get_row_data(GTK_CLIST(gl_config->mapl),
  445. GPOINTER_TO_INT(maps->data)));
  446. nbp = mysql_get_int(gl_config->mysql,
  447. "SELECT joueurs FROM Maps where id=%d", mat->mapid);
  448. mat->wids = g_malloc(sizeof(*(mat->wids)) * (nbp + 1));
  449. tbl = gtk_table_new(4, nbp + 1, FALSE);
  450. gtk_widget_show(tbl);
  451. dialog = create_dialog("Lancement d'une partie", tbl, " Valider ",
  452. " Annuler ", run_match_commit, mat, destroy_widget);
  453. gtk_table_attach_defaults(GTK_TABLE(tbl), create_label("Graphismes: "),
  454. 0, 1, 0, 1);
  455. gtk_table_attach_defaults(GTK_TABLE(tbl), create_label(" Port: "),
  456. 2, 3, 0, 1);
  457. gtk_table_attach_defaults(GTK_TABLE(tbl), create_label(" Tours: "),
  458. 0, 1, 1, 2);
  459. gtk_table_attach_defaults(GTK_TABLE(tbl), create_label(" Publique: "),
  460. 2, 3, 1, 2);
  461. mat->gfx = create_check(TRUE);
  462. gtk_tooltips_set_tip(gl_config->tooltips, mat->gfx,
  463. "Lancer un client graphique", "");
  464. mat->port = create_spin(1024, 65535, 4242);
  465. gtk_tooltips_set_tip(gl_config->tooltips, mat->port,
  466. "Port d'écoute du serveur", "");
  467. mat->public = create_check(TRUE);
  468. gtk_tooltips_set_tip(gl_config->tooltips, mat->public,
  469. "Autorisé les connexions d'autres clients graphiques ?",
  470. "");
  471. mat->turns = create_spin(1, 5000, 200);
  472. gtk_tooltips_set_tip(gl_config->tooltips, mat->turns,
  473. "Nombre de tours que durera la partie", "");
  474. gtk_table_attach_defaults(GTK_TABLE(tbl), mat->gfx, 1, 2, 0, 1);
  475. gtk_table_attach_defaults(GTK_TABLE(tbl), mat->port, 3, 4, 0, 1);
  476. gtk_table_attach_defaults(GTK_TABLE(tbl), mat->turns, 1, 2, 1, 2);
  477. gtk_table_attach_defaults(GTK_TABLE(tbl), mat->public, 3, 4, 1, 2);
  478. gtk_table_attach_defaults(GTK_TABLE(tbl), create_label("Paramètres: "),
  479. 0, 1, 2, 3);
  480. mat->cmd = create_entry(NULL, 100);
  481. gtk_signal_connect(GTK_OBJECT(mat->cmd), "activate",
  482. run_match_commit, mat);
  483. gtk_signal_connect_after(GTK_OBJECT(mat->cmd), "activate",
  484. destroy_widget, dialog);
  485. gtk_tooltips_set_tip(gl_config->tooltips, mat->cmd,
  486. "Paramètres supplémentaires à passer au serveur", "");
  487. gtk_table_attach_defaults(GTK_TABLE(tbl), mat->cmd, 1, 4, 2, 3);
  488. wid = gtk_hseparator_new();
  489. gtk_widget_show(wid);
  490. gtk_table_attach(GTK_TABLE(tbl), wid, 0, 4, 3, 4, GTK_FILL, GTK_FILL, 2, 5);
  491. for (i = 0, l = champ; i < nbp; i++)
  492. {
  493. snprintf(buf, MYSQL_BUFFER_SIZE, "Joueur n°%d: ", i);
  494. gtk_table_attach_defaults(GTK_TABLE(tbl), create_label(buf),
  495. 0, 1, i + 4, i + 5);
  496. wid = gtk_combo_new();
  497. gtk_widget_show(wid);
  498. gtk_tooltips_set_tip(gl_config->tooltips, GTK_COMBO(wid)->entry,
  499. "Choix des champions à utiliser", "");
  500. gtk_combo_set_popdown_strings(GTK_COMBO(wid), champ);
  501. gtk_combo_set_value_in_list(GTK_COMBO(wid), TRUE, TRUE);
  502. gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(wid)->entry), l->data);
  503. gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(wid)->entry), FALSE);
  504. gtk_table_attach_defaults(GTK_TABLE(tbl), wid, 1, 4, i + 4, i + 5);
  505. mat->wids[i] = GTK_COMBO(wid)->entry;
  506. if (l->next != NULL)
  507. l = l->next;
  508. }
  509. mat->wids[nbp] = NULL;
  510. for (l = champ; l != NULL; l = l->next)
  511. g_free(l->data);
  512. g_list_free(champ);
  513. return dialog;
  514. }