More shuffling of GTK-related routines to gtk subdirectory.
[obnox/wireshark/wip.git] / prefs.c
1 /* prefs.c
2  * Routines for handling preferences
3  *
4  * $Id: prefs.c,v 1.22 1999/09/09 03:31:50 gram 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   }
172
173   if (! pf_path) {
174     pf_path = (gchar *) g_malloc(strlen(getenv("HOME")) + strlen(PF_DIR) +
175       strlen(PF_NAME) + 4);
176     sprintf(pf_path, "%s/%s/%s", getenv("HOME"), PF_DIR, PF_NAME);
177   }
178     
179   *pf_path_return = NULL;
180   if ((pf = fopen(pf_path, "r")) == NULL) {
181     if (errno != ENOENT)
182       *pf_path_return = pf_path;
183     return &prefs;
184   }
185     
186   while ((got_c = getc(pf)) != EOF) {
187     if (got_c == '\n') {
188       state = START;
189       fline++;
190       continue;
191     }
192     if (var_len >= MAX_VAR_LEN) {
193       g_warning ("%s line %d: Variable too long", pf_path, fline);
194       state = IN_SKIP;
195       var_len = 0;
196       continue;
197     }
198     if (val_len >= MAX_VAL_LEN) {
199       g_warning ("%s line %d: Value too long", pf_path, fline);
200       state = IN_SKIP;
201       var_len = 0;
202       continue;
203     }
204     
205     switch (state) {
206       case START:
207         if (isalnum(got_c)) {
208           if (var_len > 0) {
209             if (got_val) {
210               cur_var[var_len] = '\0';
211               cur_val[val_len] = '\0';
212               if (! set_pref(cur_var, cur_val))
213                 g_warning ("%s line %d: Bogus preference", pf_path, pline);
214             } else {
215               g_warning ("%s line %d: Incomplete preference", pf_path, pline);
216             }
217           }
218           state      = IN_VAR;
219           got_val    = FALSE;
220           cur_var[0] = got_c;
221           var_len    = 1;
222           pline = fline;
223         } else if (isspace(got_c) && var_len > 0 && got_val) {
224           state = PRE_VAL;
225         } else if (got_c == '#') {
226           state = IN_SKIP;
227         } else {
228           g_warning ("%s line %d: Malformed line", pf_path, fline);
229         }
230         break;
231       case IN_VAR:
232         if (got_c != ':') {
233           cur_var[var_len] = got_c;
234           var_len++;
235         } else {
236           state   = PRE_VAL;
237           val_len = 0;
238           got_val = TRUE;
239         }
240         break;
241       case PRE_VAL:
242         if (!isspace(got_c)) {
243           state = IN_VAL;
244           cur_val[val_len] = got_c;
245           val_len++;
246         }
247         break;
248       case IN_VAL:
249         if (got_c != '#')  {
250           cur_val[val_len] = got_c;
251           val_len++;
252         } else {
253           while (isspace(cur_val[val_len]) && val_len > 0)
254             val_len--;
255           state = IN_SKIP;
256         }
257         break;
258     }
259   }
260   if (var_len > 0) {
261     if (got_val) {
262       cur_var[var_len] = '\0';
263       cur_val[val_len] = '\0';
264       if (! set_pref(cur_var, cur_val))
265         g_warning ("%s line %d: Bogus preference", pf_path, pline);
266     } else {
267       g_warning ("%s line %d: Incomplete preference", pf_path, pline);
268     }
269   }
270   fclose(pf);
271   
272   return &prefs;
273 }
274
275 #define PRS_PRINT_FMT  "print.format"
276 #define PRS_PRINT_DEST "print.destination"
277 #define PRS_PRINT_FILE "print.file"
278 #define PRS_PRINT_CMD  "print.command"
279 #define PRS_COL_FMT    "column.format"
280
281 static gchar *pr_formats[] = { "text", "postscript" };
282 static gchar *pr_dests[]   = { "command", "file" };
283
284 int
285 set_pref(gchar *pref, gchar *value) {
286   GList    *col_l;
287   gint      llen;
288   fmt_data *cfmt;
289
290   if (strcmp(pref, PRS_PRINT_FMT) == 0) {
291     if (strcmp(value, pr_formats[PR_FMT_TEXT]) == 0) {
292       prefs.pr_format = PR_FMT_TEXT;
293     } else if (strcmp(value, pr_formats[PR_FMT_PS]) == 0) {
294       prefs.pr_format = PR_FMT_PS;
295     } else {
296       return 0;
297     }
298   } else if (strcmp(pref, PRS_PRINT_DEST) == 0) {
299     if (strcmp(value, pr_dests[PR_DEST_CMD]) == 0) {
300       prefs.pr_dest = PR_DEST_CMD;
301     } else if (strcmp(value, pr_dests[PR_DEST_FILE]) == 0) {
302       prefs.pr_dest = PR_DEST_FILE;
303     } else {
304       return 0;
305     }
306   } else if (strcmp(pref, PRS_PRINT_FILE) == 0) {
307     if (prefs.pr_file) g_free(prefs.pr_file);
308     prefs.pr_file = g_strdup(value);
309   } else if (strcmp(pref, PRS_PRINT_CMD) == 0) {
310     if (prefs.pr_cmd) g_free(prefs.pr_cmd);
311     prefs.pr_cmd = g_strdup(value);
312   } else if (strcmp(pref, PRS_COL_FMT) == 0) {
313     if ((col_l = get_string_list(value)) && (g_list_length(col_l) % 2) == 0) {
314       while (prefs.col_list) {
315         cfmt = prefs.col_list->data;
316         g_free(cfmt->title);
317         g_free(cfmt->fmt);
318         g_free(cfmt);
319         prefs.col_list = g_list_remove_link(prefs.col_list, prefs.col_list);
320       }
321       llen             = g_list_length(col_l);
322       prefs.num_cols   = llen / 2;
323       col_l = g_list_first(col_l);
324       while(col_l) {
325         cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
326         cfmt->title    = g_strdup(col_l->data);
327         col_l          = col_l->next;
328         cfmt->fmt      = g_strdup(col_l->data);
329         col_l          = col_l->next;
330         prefs.col_list = g_list_append(prefs.col_list, cfmt);
331       }
332       /* To do: else print some sort of error? */
333     }
334     clear_string_list(col_l);
335   } else {
336     return 0;
337   }
338   
339   return 1;
340 }
341
342 void
343 write_prefs(void) {
344   FILE        *pf;
345   struct stat  s_buf;
346   
347   /* To do:
348    * - Split output lines longer than MAX_VAL_LEN
349    * - Create a function for the preference directory check/creation
350    *   so that duplication can be avoided with filter.c
351    */
352
353   if (! pf_path) {
354     pf_path = (gchar *) g_malloc(strlen(getenv("HOME")) + strlen(PF_DIR) +
355       strlen(PF_NAME) + 4);
356   }
357
358   sprintf(pf_path, "%s/%s", getenv("HOME"), PF_DIR);
359   if (stat(pf_path, &s_buf) != 0)
360 #ifdef WIN32
361     mkdir(pf_path);
362 #else
363     mkdir(pf_path, 0755);
364 #endif
365
366   sprintf(pf_path, "%s/%s/%s", getenv("HOME"), PF_DIR, PF_NAME);
367   if ((pf = fopen(pf_path, "w")) == NULL) {
368      simple_dialog(ESD_TYPE_WARN, NULL,
369       "Can't open preferences file\n\"%s\".", pf_path);
370    return;
371  }
372     
373   fputs("# Configuration file for Ethereal " VERSION ".\n"
374     "#\n"
375     "# This file is regenerated each time preferences are saved within\n"
376     "# Ethereal.  Making manual changes should be safe, however.\n"
377     "\n"
378     "######## Printing ########\n"
379     "\n", pf);
380
381   fprintf (pf, "# Can be one of \"text\" or \"postscript\".\n"
382     "print.format: %s\n\n", pr_formats[prefs.pr_format]);
383
384   fprintf (pf, "# Can be one of \"command\" or \"file\".\n"
385     "print.destination: %s\n\n", pr_dests[prefs.pr_dest]);
386
387   fprintf (pf, "# This is the file that gets written to when the "
388     "destination is set to \"file\"\n"
389     "%s: %s\n\n", PRS_PRINT_FILE, prefs.pr_file);
390
391   fprintf (pf, "# Output gets piped to this command when the destination "
392     "is set to \"command\"\n"
393     "%s: %s\n\n", PRS_PRINT_CMD, prefs.pr_cmd);
394
395   fprintf (pf, "# Packet list column format.  Each pair of strings consists "
396     "of a column title \n# and its format.\n"
397     "%s: %s\n\n", PRS_COL_FMT, col_format_to_pref_str());
398
399   fclose(pf);
400 }