Settings.c 10.4 KB
Newer Older
Hisham Muhammad's avatar
Hisham Muhammad committed
1
2
/*
htop - Settings.c
Hisham Muhammad's avatar
Hisham Muhammad committed
3
(C) 2004-2011 Hisham H. Muhammad
Hisham Muhammad's avatar
Hisham Muhammad committed
4
5
6
7
8
9
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/

#include "Settings.h"

Hisham Muhammad's avatar
Hisham Muhammad committed
10
11
#include "String.h"
#include "Vector.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
12

Hisham Muhammad's avatar
Hisham Muhammad committed
13
14
15
16
17
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

Hisham Muhammad's avatar
Hisham Muhammad committed
18
19
20
#define DEFAULT_DELAY 15

/*{
Hisham Muhammad's avatar
Hisham Muhammad committed
21
22
23
#include "ProcessList.h"
#include "Header.h"
#include <stdbool.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
24
25
26
27
28
29
30

typedef struct Settings_ {
   char* userSettings;
   ProcessList* pl;
   Header* header;
   int colorScheme;
   int delay;
Hisham Muhammad's avatar
Hisham Muhammad committed
31
   bool changed;
Hisham Muhammad's avatar
Hisham Muhammad committed
32
33
34
35
36
37
38
39
40
} Settings;

}*/

void Settings_delete(Settings* this) {
   free(this->userSettings);
   free(this);
}

41
static void Settings_readMeters(Settings* this, char* line, HeaderSide side) {
Hisham Muhammad's avatar
Hisham Muhammad committed
42
   char* trim = String_trim(line);
43
44
   int nIds;
   char** ids = String_split(trim, ' ', &nIds);
Hisham Muhammad's avatar
Hisham Muhammad committed
45
   free(trim);
46
   for (int i = 0; ids[i]; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
47
48
49
50
51
      Header_createMeter(this->header, ids[i], side);
   }
   String_freeArray(ids);
}

52
static void Settings_readMeterModes(Settings* this, char* line, HeaderSide side) {
Hisham Muhammad's avatar
Hisham Muhammad committed
53
   char* trim = String_trim(line);
54
55
   int nIds;
   char** ids = String_split(trim, ' ', &nIds);
Hisham Muhammad's avatar
Hisham Muhammad committed
56
   free(trim);
57
   for (int i = 0; ids[i]; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
58
59
60
61
62
63
      int mode = atoi(ids[i]);
      Header_setMode(this->header, i, mode, side);
   }
   String_freeArray(ids);
}

64
static bool Settings_read(Settings* this, char* fileName, int cpuCount) {
65
66
   FILE* fd = fopen(fileName, "r");
   if (!fd)
Hisham Muhammad's avatar
Hisham Muhammad committed
67
      return false;
68
   
Hisham Muhammad's avatar
Hisham Muhammad committed
69
   const int maxLine = 2048;
Hisham Muhammad's avatar
Hisham Muhammad committed
70
71
   char buffer[maxLine];
   bool readMeters = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
72
   while (fgets(buffer, maxLine, fd)) {
73
74
75
76
77
78
      int nOptions;
      char** option = String_split(buffer, '=', &nOptions);
      if (nOptions < 2) {
         String_freeArray(option);
         continue;
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
79
80
      if (String_eq(option[0], "fields")) {
         char* trim = String_trim(option[1]);
81
82
         int nIds;
         char** ids = String_split(trim, ' ', &nIds);
Hisham Muhammad's avatar
Hisham Muhammad committed
83
84
         free(trim);
         int i, j;
85
         this->pl->flags = 0;
86
         for (j = 0, i = 0; i < LAST_PROCESSFIELD && ids[i]; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
87
88
89
90
            // This "+1" is for compatibility with the older enum format.
            int id = atoi(ids[i]) + 1;
            if (id > 0 && id < LAST_PROCESSFIELD) {
               this->pl->fields[j] = id;
91
               this->pl->flags |= Process_fieldFlags[id];
Hisham Muhammad's avatar
Hisham Muhammad committed
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
               j++;
            }
         }
         this->pl->fields[j] = (ProcessField) NULL;
         String_freeArray(ids);
      } else if (String_eq(option[0], "sort_key")) {
         // This "+1" is for compatibility with the older enum format.
         this->pl->sortKey = atoi(option[1]) + 1;
      } else if (String_eq(option[0], "sort_direction")) {
         this->pl->direction = atoi(option[1]);
      } else if (String_eq(option[0], "tree_view")) {
         this->pl->treeView = atoi(option[1]);
      } else if (String_eq(option[0], "hide_threads")) {
         this->pl->hideThreads = atoi(option[1]);
      } else if (String_eq(option[0], "hide_kernel_threads")) {
         this->pl->hideKernelThreads = atoi(option[1]);
      } else if (String_eq(option[0], "hide_userland_threads")) {
         this->pl->hideUserlandThreads = atoi(option[1]);
      } else if (String_eq(option[0], "shadow_other_users")) {
         this->pl->shadowOtherUsers = atoi(option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
112
113
      } else if (String_eq(option[0], "show_thread_names")) {
         this->pl->showThreadNames = atoi(option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
114
115
116
117
      } else if (String_eq(option[0], "highlight_base_name")) {
         this->pl->highlightBaseName = atoi(option[1]);
      } else if (String_eq(option[0], "highlight_megabytes")) {
         this->pl->highlightMegabytes = atoi(option[1]);
118
119
      } else if (String_eq(option[0], "highlight_threads")) {
         this->pl->highlightThreads = atoi(option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
120
121
      } else if (String_eq(option[0], "header_margin")) {
         this->header->margin = atoi(option[1]);
122
      } else if (String_eq(option[0], "expand_system_time")) {
123
124
125
126
         // Compatibility option.
         this->pl->detailedCPUTime = atoi(option[1]);
      } else if (String_eq(option[0], "detailed_cpu_time")) {
         this->pl->detailedCPUTime = atoi(option[1]);
127
128
      } else if (String_eq(option[0], "cpu_count_from_zero")) {
         this->pl->countCPUsFromZero = atoi(option[1]);
129
130
      } else if (String_eq(option[0], "update_process_names")) {
         this->pl->updateProcessNames = atoi(option[1]);
131
132
      } else if (String_eq(option[0], "account_guest_in_cpu_meter")) {
         this->pl->accountGuestInCPUMeter = atoi(option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
133
134
135
136
137
138
139
      } else if (String_eq(option[0], "delay")) {
         this->delay = atoi(option[1]);
      } else if (String_eq(option[0], "color_scheme")) {
         this->colorScheme = atoi(option[1]);
         if (this->colorScheme < 0) this->colorScheme = 0;
         if (this->colorScheme > 5) this->colorScheme = 5;
      } else if (String_eq(option[0], "left_meters")) {
140
141
         Settings_readMeters(this, option[1], LEFT_HEADER);
         readMeters = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
142
      } else if (String_eq(option[0], "right_meters")) {
143
144
         Settings_readMeters(this, option[1], RIGHT_HEADER);
         readMeters = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
145
      } else if (String_eq(option[0], "left_meter_modes")) {
146
147
         Settings_readMeterModes(this, option[1], LEFT_HEADER);
         readMeters = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
148
      } else if (String_eq(option[0], "right_meter_modes")) {
149
150
         Settings_readMeterModes(this, option[1], RIGHT_HEADER);
         readMeters = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
151
152
153
154
155
      }
      String_freeArray(option);
   }
   fclose(fd);
   if (!readMeters) {
156
      Header_defaultMeters(this->header, cpuCount);
Hisham Muhammad's avatar
Hisham Muhammad committed
157
158
159
160
161
162
163
164
165
166
167
168
   }
   return true;
}

bool Settings_write(Settings* this) {
   // TODO: implement File object and make
   // file I/O object-oriented.
   FILE* fd;
   fd = fopen(this->userSettings, "w");
   if (fd == NULL) {
      return false;
   }
169
   fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
Hisham Muhammad's avatar
Hisham Muhammad committed
170
171
172
173
174
175
176
177
178
179
180
181
182
183
   fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
   fprintf(fd, "fields=");
   for (int i = 0; this->pl->fields[i]; i++) {
      // This "-1" is for compatibility with the older enum format.
      fprintf(fd, "%d ", (int) this->pl->fields[i]-1);
   }
   fprintf(fd, "\n");
   // This "-1" is for compatibility with the older enum format.
   fprintf(fd, "sort_key=%d\n", (int) this->pl->sortKey-1);
   fprintf(fd, "sort_direction=%d\n", (int) this->pl->direction);
   fprintf(fd, "hide_threads=%d\n", (int) this->pl->hideThreads);
   fprintf(fd, "hide_kernel_threads=%d\n", (int) this->pl->hideKernelThreads);
   fprintf(fd, "hide_userland_threads=%d\n", (int) this->pl->hideUserlandThreads);
   fprintf(fd, "shadow_other_users=%d\n", (int) this->pl->shadowOtherUsers);
Hisham Muhammad's avatar
Hisham Muhammad committed
184
   fprintf(fd, "show_thread_names=%d\n", (int) this->pl->showThreadNames);
Hisham Muhammad's avatar
Hisham Muhammad committed
185
186
   fprintf(fd, "highlight_base_name=%d\n", (int) this->pl->highlightBaseName);
   fprintf(fd, "highlight_megabytes=%d\n", (int) this->pl->highlightMegabytes);
187
   fprintf(fd, "highlight_threads=%d\n", (int) this->pl->highlightThreads);
Hisham Muhammad's avatar
Hisham Muhammad committed
188
189
   fprintf(fd, "tree_view=%d\n", (int) this->pl->treeView);
   fprintf(fd, "header_margin=%d\n", (int) this->header->margin);
190
   fprintf(fd, "detailed_cpu_time=%d\n", (int) this->pl->detailedCPUTime);
191
   fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->pl->countCPUsFromZero);
192
   fprintf(fd, "update_process_names=%d\n", (int) this->pl->updateProcessNames);
193
   fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->pl->accountGuestInCPUMeter);
Hisham Muhammad's avatar
Hisham Muhammad committed
194
195
196
197
   fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme);
   fprintf(fd, "delay=%d\n", (int) this->delay);
   fprintf(fd, "left_meters=");
   for (int i = 0; i < Header_size(this->header, LEFT_HEADER); i++) {
198
199
200
      char* name = Header_readMeterName(this->header, i, LEFT_HEADER);
      fprintf(fd, "%s ", name);
      free(name);
Hisham Muhammad's avatar
Hisham Muhammad committed
201
202
203
204
205
206
207
   }
   fprintf(fd, "\n");
   fprintf(fd, "left_meter_modes=");
   for (int i = 0; i < Header_size(this->header, LEFT_HEADER); i++)
      fprintf(fd, "%d ", Header_readMeterMode(this->header, i, LEFT_HEADER));
   fprintf(fd, "\n");
   fprintf(fd, "right_meters=");
208
209
210
211
212
   for (int i = 0; i < Header_size(this->header, RIGHT_HEADER); i++) {
      char* name = Header_readMeterName(this->header, i, RIGHT_HEADER);
      fprintf(fd, "%s ", name);
      free(name);
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
213
214
215
216
   fprintf(fd, "\n");
   fprintf(fd, "right_meter_modes=");
   for (int i = 0; i < Header_size(this->header, RIGHT_HEADER); i++)
      fprintf(fd, "%d ", Header_readMeterMode(this->header, i, RIGHT_HEADER));
217
   fprintf(fd, "\n");
Hisham Muhammad's avatar
Hisham Muhammad committed
218
219
220
   fclose(fd);
   return true;
}
221

222
Settings* Settings_new(ProcessList* pl, Header* header, int cpuCount) {
223
224
225
   Settings* this = malloc(sizeof(Settings));
   this->pl = pl;
   this->header = header;
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
   char* legacyDotfile = NULL;
   char* rcfile = getenv("HTOPRC");
   if (rcfile) {
      this->userSettings = strdup(rcfile);
   } else {
      const char* home = getenv("HOME");
      if (!home) home = "";
      const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
      char* configDir = NULL;
      char* htopDir = NULL;
      if (xdgConfigHome) {
         this->userSettings = String_cat(xdgConfigHome, "/htop/htoprc");
         configDir = strdup(xdgConfigHome);
         htopDir = String_cat(xdgConfigHome, "/htop");
      } else {
         this->userSettings = String_cat(home, "/.config/htop/htoprc");
         configDir = String_cat(home, "/.config");
         htopDir = String_cat(home, "/.config/htop");
      }
      legacyDotfile = String_cat(home, "/.htoprc");
246
247
      (void) mkdir(configDir, 0700);
      (void) mkdir(htopDir, 0700);
248
249
      free(htopDir);
      free(configDir);
250
      struct stat st;
251
252
253
      if (lstat(legacyDotfile, &st) != 0) {
         st.st_mode = 0;
      }
254
      if (access(legacyDotfile, R_OK) != 0 || S_ISLNK(st.st_mode)) {
255
256
257
258
         free(legacyDotfile);
         legacyDotfile = NULL;
      }
   }
259
260
261
   this->colorScheme = 0;
   this->changed = false;
   this->delay = DEFAULT_DELAY;
262
263
264
265
266
267
268
269
270
   bool ok = Settings_read(this, legacyDotfile ? legacyDotfile : this->userSettings, cpuCount);
   if (ok) {
      if (legacyDotfile) {
         // Transition to new location and delete old configuration file
         if (Settings_write(this))
            unlink(legacyDotfile);
         free(legacyDotfile);
      }
   } else {
271
272
273
      this->changed = true;
      // TODO: how to get SYSCONFDIR correctly through Autoconf?
      char* systemSettings = String_cat(SYSCONFDIR, "/htoprc");
274
      ok = Settings_read(this, systemSettings, cpuCount);
275
276
      free(systemSettings);
      if (!ok) {
277
         Header_defaultMeters(this->header, cpuCount);
278
279
280
281
282
283
284
         pl->hideKernelThreads = true;
         pl->highlightMegabytes = true;
         pl->highlightThreads = false;
      }
   }
   return this;
}