Add stream window color preferences. We don't (yet) use cmaps, so this
[obnox/wireshark/wip.git] / prefs.c
1 /* prefs.c
2  * Routines for handling preferences
3  *
4  * $Id: prefs.c,v 1.23 1999/12/02 04:30:03 gerald Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33
34 #ifdef HAVE_DIRECT_H
35 #include <direct.h>
36 #endif
37
38 #include <stdlib.h>
39 #include <ctype.h>
40 #include <errno.h>
41
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45
46 #include <sys/stat.h>
47
48 #include "globals.h"
49 #include "packet.h"
50 #include "file.h"
51 #include "prefs.h"
52 #include "column.h"
53 #include "print.h"
54 #include "util.h"
55
56 /* Internal functions */
57 static int    set_pref(gchar*, gchar*);
58 static GList *get_string_list(gchar *);
59 static void   clear_string_list(GList *);
60
61 #define PF_NAME "preferences"
62
63 static int init_prefs = 1;
64 static gchar *pf_path = NULL;
65
66 /* Parse through a list of comma-separated, quoted strings.  Return a
67    list of the string data */
68 static GList *
69 get_string_list(gchar *str) {
70   enum { PRE_QUOT, IN_QUOT, POST_QUOT };
71
72   gint      state = PRE_QUOT, i = 0, j = 0;
73   gboolean  backslash = FALSE;
74   gchar     cur_c, *slstr = NULL;
75   GList    *sl = NULL;
76   
77   while ((cur_c = str[i]) != '\0') {
78     if (cur_c == '"' && ! backslash) {
79       switch (state) {
80         case PRE_QUOT:
81           state = IN_QUOT;
82           slstr = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
83           j = 0;
84           break;
85         case IN_QUOT:
86           state  = POST_QUOT;
87           slstr[j] = '\0';
88           sl = g_list_append(sl, slstr);
89           break;
90         case POST_QUOT:
91           clear_string_list(sl);
92           return NULL;
93           break;
94         default:
95           break;
96       }
97     } else if (cur_c == '\\' && ! backslash) {
98       backslash = TRUE;
99     } else if (cur_c == ',' && state == POST_QUOT) {
100       state = PRE_QUOT;
101     } else if (state == IN_QUOT && j < COL_MAX_LEN) {
102       slstr[j] = str[i];
103       j++;
104     }
105     i++;
106   }
107   if (state != POST_QUOT) {
108     clear_string_list(sl);
109   }
110   return(sl);
111 }
112
113 void
114 clear_string_list(GList *sl) {
115   GList *l = sl;
116   
117   while (l) {
118     g_free(l->data);
119     l = g_list_remove_link(l, l);
120   }
121 }
122
123 /* Preferences file format:
124  * - Configuration directives start at the beginning of the line, and 
125  *   are terminated with a colon.
126  * - Directives can be continued on the next line by preceding them with
127  *   whitespace.
128  *
129  * Example:
130
131 # This is a comment line
132 print.command: lpr
133 print.file: /a/very/long/path/
134         to/ethereal-out.ps
135  *
136  */
137
138 #define MAX_VAR_LEN    32
139 #define MAX_VAL_LEN  1024
140 #define DEF_NUM_COLS    6
141 e_prefs *
142 read_prefs(char **pf_path_return) {
143   enum { START, IN_VAR, PRE_VAL, IN_VAL, IN_SKIP };
144   FILE     *pf;
145   gchar     cur_var[MAX_VAR_LEN], cur_val[MAX_VAL_LEN];
146   int       got_c, state = START, i;
147   gint      var_len = 0, val_len = 0, fline = 1, pline = 1;
148   gboolean  got_val = FALSE;
149   fmt_data *cfmt;
150   gchar    *col_fmt[] = {"No.",      "%m", "Time",        "%t",
151                          "Source",   "%s", "Destination", "%d",
152                          "Protocol", "%p", "Info",        "%i"};
153
154   
155   /* Initialize preferences.  With any luck, these values will be
156      overwritten below. */
157   if (init_prefs) {
158     init_prefs       = 0;
159     prefs.pr_format  = PR_FMT_TEXT;
160     prefs.pr_dest    = PR_DEST_CMD;
161     prefs.pr_file    = g_strdup("ethereal.out");
162     prefs.pr_cmd     = g_strdup("lpr");
163     prefs.col_list = NULL;
164     for (i = 0; i < DEF_NUM_COLS; i++) {
165       cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
166       cfmt->title = g_strdup(col_fmt[i * 2]);
167       cfmt->fmt   = g_strdup(col_fmt[(i * 2) + 1]);
168       prefs.col_list = g_list_append(prefs.col_list, cfmt);
169     }
170     prefs.num_cols  = DEF_NUM_COLS;
171     prefs.st_client_fg.pixel =     0;
172     prefs.st_client_fg.red   = 32767;
173     prefs.st_client_fg.green =     0;
174     prefs.st_client_fg.blue  =     0;
175     prefs.st_client_bg.pixel = 65535;
176     prefs.st_client_bg.red   = 65535;
177     prefs.st_client_bg.green = 65535;
178     prefs.st_client_bg.blue  = 65535;
179     prefs.st_server_fg.pixel =     0;
180     prefs.st_server_fg.red   =     0;
181     prefs.st_server_fg.green =     0;
182     prefs.st_server_fg.blue  = 32767;
183     prefs.st_server_bg.pixel = 65535;
184     prefs.st_server_bg.red   = 65535;
185     prefs.st_server_bg.green = 65535;
186     prefs.st_server_bg.blue  = 65535;
187   }
188
189   if (! pf_path) {
190     pf_path = (gchar *) g_malloc(strlen(getenv("HOME")) + strlen(PF_DIR) +
191       strlen(PF_NAME) + 4);
192     sprintf(pf_path, "%s/%s/%s", getenv("HOME"), PF_DIR, PF_NAME);
193   }
194     
195   *pf_path_return = NULL;
196   if ((pf = fopen(pf_path, "r")) == NULL) {
197     if (errno != ENOENT)
198       *pf_path_return = pf_path;
199     return &prefs;
200   }
201     
202   while ((got_c = getc(pf)) != EOF) {
203     if (got_c == '\n') {
204       state = START;
205       fline++;
206       continue;
207     }
208     if (var_len >= MAX_VAR_LEN) {
209       g_warning ("%s line %d: Variable too long", pf_path, fline);
210       state = IN_SKIP;
211       var_len = 0;
212       continue;
213     }
214     if (val_len >= MAX_VAL_LEN) {
215       g_warning ("%s line %d: Value too long", pf_path, fline);
216       state = IN_SKIP;
217       var_len = 0;
218       continue;
219     }
220     
221     switch (state) {
222       case START:
223         if (isalnum(got_c)) {
224           if (var_len > 0) {
225             if (got_val) {
226               cur_var[var_len] = '\0';
227               cur_val[val_len] = '\0';
228               if (! set_pref(cur_var, cur_val))
229                 g_warning ("%s line %d: Bogus preference", pf_path, pline);
230             } else {
231               g_warning ("%s line %d: Incomplete preference", pf_path, pline);
232             }
233           }
234           state      = IN_VAR;
235           got_val    = FALSE;
236           cur_var[0] = got_c;
237           var_len    = 1;
238           pline = fline;
239         } else if (isspace(got_c) && var_len > 0 && got_val) {
240           state = PRE_VAL;
241         } else if (got_c == '#') {
242           state = IN_SKIP;
243         } else {
244           g_warning ("%s line %d: Malformed line", pf_path, fline);
245         }
246         break;
247       case IN_VAR:
248         if (got_c != ':') {
249           cur_var[var_len] = got_c;
250           var_len++;
251         } else {
252           state   = PRE_VAL;
253           val_len = 0;
254           got_val = TRUE;
255         }
256         break;
257       case PRE_VAL:
258         if (!isspace(got_c)) {
259           state = IN_VAL;
260           cur_val[val_len] = got_c;
261           val_len++;
262         }
263         break;
264       case IN_VAL:
265         if (got_c != '#')  {
266           cur_val[val_len] = got_c;
267           val_len++;
268         } else {
269           while (isspace(cur_val[val_len]) && val_len > 0)
270             val_len--;
271           state = IN_SKIP;
272         }
273         break;
274     }
275   }
276   if (var_len > 0) {
277     if (got_val) {
278       cur_var[var_len] = '\0';
279       cur_val[val_len] = '\0';
280       if (! set_pref(cur_var, cur_val))
281         g_warning ("%s line %d: Bogus preference", pf_path, pline);
282     } else {
283       g_warning ("%s line %d: Incomplete preference", pf_path, pline);
284     }
285   }
286   fclose(pf);
287   
288   return &prefs;
289 }
290
291 #define PRS_PRINT_FMT    "print.format"
292 #define PRS_PRINT_DEST   "print.destination"
293 #define PRS_PRINT_FILE   "print.file"
294 #define PRS_PRINT_CMD    "print.command"
295 #define PRS_COL_FMT      "column.format"
296 #define PRS_STREAM_CL_FG "stream.client.fg"
297 #define PRS_STREAM_CL_BG "stream.client.bg"
298 #define PRS_STREAM_SR_FG "stream.server.fg"
299 #define PRS_STREAM_SR_BG "stream.server.bg"
300
301 #define RED_COMPONENT(x)   ((((x) >> 16) & 0xff) * 65535 / 255)
302 #define GREEN_COMPONENT(x) ((((x) >>  8) & 0xff) * 65535 / 255)
303 #define BLUE_COMPONENT(x)   (((x)        & 0xff) * 65535 / 255)
304
305 static gchar *pr_formats[] = { "text", "postscript" };
306 static gchar *pr_dests[]   = { "command", "file" };
307
308 int
309 set_pref(gchar *pref, gchar *value) {
310   GList    *col_l;
311   gint      llen;
312   fmt_data *cfmt;
313   unsigned long int cval;
314
315   if (strcmp(pref, PRS_PRINT_FMT) == 0) {
316     if (strcmp(value, pr_formats[PR_FMT_TEXT]) == 0) {
317       prefs.pr_format = PR_FMT_TEXT;
318     } else if (strcmp(value, pr_formats[PR_FMT_PS]) == 0) {
319       prefs.pr_format = PR_FMT_PS;
320     } else {
321       return 0;
322     }
323   } else if (strcmp(pref, PRS_PRINT_DEST) == 0) {
324     if (strcmp(value, pr_dests[PR_DEST_CMD]) == 0) {
325       prefs.pr_dest = PR_DEST_CMD;
326     } else if (strcmp(value, pr_dests[PR_DEST_FILE]) == 0) {
327       prefs.pr_dest = PR_DEST_FILE;
328     } else {
329       return 0;
330     }
331   } else if (strcmp(pref, PRS_PRINT_FILE) == 0) {
332     if (prefs.pr_file) g_free(prefs.pr_file);
333     prefs.pr_file = g_strdup(value);
334   } else if (strcmp(pref, PRS_PRINT_CMD) == 0) {
335     if (prefs.pr_cmd) g_free(prefs.pr_cmd);
336     prefs.pr_cmd = g_strdup(value);
337   } else if (strcmp(pref, PRS_COL_FMT) == 0) {
338     if ((col_l = get_string_list(value)) && (g_list_length(col_l) % 2) == 0) {
339       while (prefs.col_list) {
340         cfmt = prefs.col_list->data;
341         g_free(cfmt->title);
342         g_free(cfmt->fmt);
343         g_free(cfmt);
344         prefs.col_list = g_list_remove_link(prefs.col_list, prefs.col_list);
345       }
346       llen             = g_list_length(col_l);
347       prefs.num_cols   = llen / 2;
348       col_l = g_list_first(col_l);
349       while(col_l) {
350         cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
351         cfmt->title    = g_strdup(col_l->data);
352         col_l          = col_l->next;
353         cfmt->fmt      = g_strdup(col_l->data);
354         col_l          = col_l->next;
355         prefs.col_list = g_list_append(prefs.col_list, cfmt);
356       }
357       /* To do: else print some sort of error? */
358     }
359     clear_string_list(col_l);
360   } else if (strcmp(pref, PRS_STREAM_CL_FG) == 0) {
361     cval = strtoul(value, NULL, 16);
362     prefs.st_client_fg.pixel = 0;
363     prefs.st_client_fg.red   = RED_COMPONENT(cval);
364     prefs.st_client_fg.green = GREEN_COMPONENT(cval);
365     prefs.st_client_fg.blue  = BLUE_COMPONENT(cval);
366   } else if (strcmp(pref, PRS_STREAM_CL_BG) == 0) {
367     cval = strtoul(value, NULL, 16);
368     prefs.st_client_bg.pixel = 0;
369     prefs.st_client_bg.red   = RED_COMPONENT(cval);
370     prefs.st_client_bg.green = GREEN_COMPONENT(cval);
371     prefs.st_client_bg.blue  = BLUE_COMPONENT(cval);
372   } else if (strcmp(pref, PRS_STREAM_SR_FG) == 0) {
373     cval = strtoul(value, NULL, 16);
374     prefs.st_server_fg.pixel = 0;
375     prefs.st_server_fg.red   = RED_COMPONENT(cval);
376     prefs.st_server_fg.green = GREEN_COMPONENT(cval);
377     prefs.st_server_fg.blue  = BLUE_COMPONENT(cval);
378   } else if (strcmp(pref, PRS_STREAM_SR_BG) == 0) {
379     cval = strtoul(value, NULL, 16);
380     prefs.st_server_bg.pixel = 0;
381     prefs.st_server_bg.red   = RED_COMPONENT(cval);
382     prefs.st_server_bg.green = GREEN_COMPONENT(cval);
383     prefs.st_server_bg.blue  = BLUE_COMPONENT(cval);
384   } else {
385     return 0;
386   }
387   
388   return 1;
389 }
390
391 void
392 write_prefs(void) {
393   FILE        *pf;
394   struct stat  s_buf;
395   
396   /* To do:
397    * - Split output lines longer than MAX_VAL_LEN
398    * - Create a function for the preference directory check/creation
399    *   so that duplication can be avoided with filter.c
400    */
401
402   if (! pf_path) {
403     pf_path = (gchar *) g_malloc(strlen(getenv("HOME")) + strlen(PF_DIR) +
404       strlen(PF_NAME) + 4);
405   }
406
407   sprintf(pf_path, "%s/%s", getenv("HOME"), PF_DIR);
408   if (stat(pf_path, &s_buf) != 0)
409 #ifdef WIN32
410     mkdir(pf_path);
411 #else
412     mkdir(pf_path, 0755);
413 #endif
414
415   sprintf(pf_path, "%s/%s/%s", getenv("HOME"), PF_DIR, PF_NAME);
416   if ((pf = fopen(pf_path, "w")) == NULL) {
417      simple_dialog(ESD_TYPE_WARN, NULL,
418       "Can't open preferences file\n\"%s\".", pf_path);
419    return;
420  }
421     
422   fputs("# Configuration file for Ethereal " VERSION ".\n"
423     "#\n"
424     "# This file is regenerated each time preferences are saved within\n"
425     "# Ethereal.  Making manual changes should be safe, however.\n"
426     "\n"
427     "######## Printing ########\n"
428     "\n", pf);
429
430   fprintf (pf, "# Can be one of \"text\" or \"postscript\".\n"
431     "print.format: %s\n\n", pr_formats[prefs.pr_format]);
432
433   fprintf (pf, "# Can be one of \"command\" or \"file\".\n"
434     "print.destination: %s\n\n", pr_dests[prefs.pr_dest]);
435
436   fprintf (pf, "# This is the file that gets written to when the "
437     "destination is set to \"file\"\n"
438     "%s: %s\n\n", PRS_PRINT_FILE, prefs.pr_file);
439
440   fprintf (pf, "# Output gets piped to this command when the destination "
441     "is set to \"command\"\n"
442     "%s: %s\n\n", PRS_PRINT_CMD, prefs.pr_cmd);
443
444   fprintf (pf, "# Packet list column format.  Each pair of strings consists "
445     "of a column title \n# and its format.\n"
446     "%s: %s\n\n", PRS_COL_FMT, col_format_to_pref_str());
447
448   fprintf (pf, "# TCP stream window color preferences.  Each value is a six "
449     "digit hexadecimal value in the form rrggbb.\n");
450   fprintf (pf, "%s: %02x%02x%02x\n", PRS_STREAM_CL_FG,
451     (prefs.st_client_fg.red * 255 / 65535),
452     (prefs.st_client_fg.green * 255 / 65535),
453     (prefs.st_client_fg.blue * 255 / 65535));
454   fprintf (pf, "%s: %02x%02x%02x\n", PRS_STREAM_CL_BG,
455     (prefs.st_client_bg.red * 255 / 65535),
456     (prefs.st_client_bg.green * 255 / 65535),
457     (prefs.st_client_bg.blue * 255 / 65535));
458   fprintf (pf, "%s: %02x%02x%02x\n", PRS_STREAM_SR_FG,
459     (prefs.st_server_fg.red * 255 / 65535),
460     (prefs.st_server_fg.green * 255 / 65535),
461     (prefs.st_server_fg.blue * 255 / 65535));
462   fprintf (pf, "%s: %02x%02x%02x\n", PRS_STREAM_SR_BG,
463     (prefs.st_server_bg.red * 255 / 65535),
464     (prefs.st_server_bg.green * 255 / 65535),
465     (prefs.st_server_bg.blue * 255 / 65535));
466
467   fclose(pf);
468 }