Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * dselect - Debian package maintenance user interface | |
3 | * methparse.cc - access method list parsing | |
4 | * | |
5 | * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> | |
6 | * Copyright © 2008-2011, 2013-2015 Guillem Jover <guillem@debian.org> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
20 | */ | |
21 | ||
22 | #include <config.h> | |
23 | #include <compat.h> | |
24 | ||
25 | #include <sys/types.h> | |
26 | #include <sys/stat.h> | |
27 | #include <sys/wait.h> | |
28 | ||
29 | #include <assert.h> | |
30 | #include <errno.h> | |
31 | #include <limits.h> | |
32 | #include <string.h> | |
33 | #include <dirent.h> | |
34 | #include <unistd.h> | |
35 | #include <stdlib.h> | |
36 | #include <stdio.h> | |
37 | ||
38 | #include <dpkg/i18n.h> | |
39 | #include <dpkg/c-ctype.h> | |
40 | #include <dpkg/dpkg.h> | |
41 | #include <dpkg/dpkg-db.h> | |
42 | ||
43 | #include "dselect.h" | |
44 | #include "bindings.h" | |
45 | #include "method.h" | |
46 | ||
47 | int noptions=0; | |
48 | struct dselect_option *options = nullptr, *coption = nullptr; | |
49 | struct method *methods = nullptr; | |
50 | ||
51 | static void DPKG_ATTR_NORET | |
52 | badmethod(const char *pathname, const char *why) | |
53 | { | |
54 | ohshit(_("syntax error in method options file '%.250s' -- %s"), pathname, why); | |
55 | } | |
56 | ||
57 | static void DPKG_ATTR_NORET | |
58 | eofmethod(const char *pathname, FILE *f, const char *why) | |
59 | { | |
60 | if (ferror(f)) | |
61 | ohshite(_("error reading options file '%.250s'"), pathname); | |
62 | badmethod(pathname,why); | |
63 | } | |
64 | ||
65 | void readmethods(const char *pathbase, dselect_option **optionspp, int *nread) { | |
66 | static const char *const methodprograms[]= { | |
67 | METHODSETUPSCRIPT, | |
68 | METHODUPDATESCRIPT, | |
69 | METHODINSTALLSCRIPT, | |
70 | nullptr | |
71 | }; | |
72 | const char *const *ccpp; | |
73 | int methodlen, c, baselen; | |
74 | char *pathinmeth, *pathbuf, *pathmeth; | |
75 | DIR *dir; | |
76 | FILE *names, *descfile; | |
77 | struct dirent *dent; | |
78 | struct varbuf vb; | |
79 | method *meth; | |
80 | dselect_option *opt; | |
81 | struct stat stab; | |
82 | ||
83 | baselen= strlen(pathbase); | |
84 | pathbuf= new char[baselen+IMETHODMAXLEN+IOPTIONMAXLEN+sizeof(OPTIONSDESCPFX)+10]; | |
85 | strcpy(pathbuf,pathbase); | |
86 | strcpy(pathbuf+baselen,"/"); | |
87 | pathmeth= pathbuf+baselen+1; | |
88 | ||
89 | dir= opendir(pathbuf); | |
90 | if (!dir) { | |
91 | if (errno == ENOENT) { | |
92 | delete[] pathbuf; | |
93 | return; | |
94 | } | |
95 | ohshite(_("unable to read '%.250s' directory for reading methods"), | |
96 | pathbuf); | |
97 | } | |
98 | ||
99 | debug(dbg_general, "readmethods('%s',...) directory open", pathbase); | |
100 | ||
101 | while ((dent = readdir(dir)) != nullptr) { | |
102 | c= dent->d_name[0]; | |
103 | debug(dbg_general, "readmethods('%s',...) considering '%s' ...", | |
104 | pathbase, dent->d_name); | |
105 | if (c != '_' && !c_isalpha(c)) | |
106 | continue; | |
107 | char *p = dent->d_name + 1; | |
108 | while ((c = *p) != 0 && c_isalnum(c) && c != '_') | |
109 | p++; | |
110 | if (c) continue; | |
111 | methodlen= strlen(dent->d_name); | |
112 | if (methodlen > IMETHODMAXLEN) | |
113 | ohshit(_("method '%.250s' has name that is too long (%d > %d characters)"), | |
114 | dent->d_name, methodlen, IMETHODMAXLEN); | |
115 | /* Check if there is a localized version of this method */ | |
116 | ||
117 | strcpy(pathmeth, dent->d_name); | |
118 | strcpy(pathmeth+methodlen, "/"); | |
119 | pathinmeth= pathmeth+methodlen+1; | |
120 | ||
121 | for (ccpp= methodprograms; *ccpp; ccpp++) { | |
122 | strcpy(pathinmeth,*ccpp); | |
123 | if (access(pathbuf,R_OK|X_OK)) | |
124 | ohshite(_("unable to access method script '%.250s'"), pathbuf); | |
125 | } | |
126 | debug(dbg_general, " readmethods('%s',...) scripts ok", pathbase); | |
127 | ||
128 | strcpy(pathinmeth,METHODOPTIONSFILE); | |
129 | names= fopen(pathbuf,"r"); | |
130 | if (!names) | |
131 | ohshite(_("unable to read method options file '%.250s'"), pathbuf); | |
132 | ||
133 | meth= new method; | |
134 | meth->name= new char[strlen(dent->d_name)+1]; | |
135 | strcpy(meth->name,dent->d_name); | |
136 | meth->path= new char[baselen+1+methodlen+2+50]; | |
137 | strncpy(meth->path,pathbuf,baselen+1+methodlen); | |
138 | strcpy(meth->path+baselen+1+methodlen,"/"); | |
139 | meth->pathinmeth= meth->path+baselen+1+methodlen+1; | |
140 | meth->next= methods; | |
141 | meth->prev = nullptr; | |
142 | if (methods) | |
143 | methods->prev = meth; | |
144 | methods= meth; | |
145 | debug(dbg_general, " readmethods('%s',...) new method" | |
146 | " name='%s' path='%s' pathinmeth='%s'", | |
147 | pathbase, meth->name, meth->path, meth->pathinmeth); | |
148 | ||
149 | while ((c= fgetc(names)) != EOF) { | |
150 | if (c_isspace(c)) | |
151 | continue; | |
152 | opt= new dselect_option; | |
153 | opt->meth= meth; | |
154 | vb.reset(); | |
155 | do { | |
156 | if (!c_isdigit(c)) | |
157 | badmethod(pathbuf, _("non-digit where digit wanted")); | |
158 | vb(c); | |
159 | c= fgetc(names); | |
160 | if (c == EOF) | |
161 | eofmethod(pathbuf, names, _("end of file in index string")); | |
162 | } while (!c_isspace(c)); | |
163 | if (strlen(vb.string()) > OPTIONINDEXMAXLEN) | |
164 | badmethod(pathbuf,_("index string too long")); | |
165 | strcpy(opt->index,vb.string()); | |
166 | do { | |
167 | if (c == '\n') badmethod(pathbuf,_("newline before option name start")); | |
168 | c= fgetc(names); | |
169 | if (c == EOF) | |
170 | eofmethod(pathbuf, names, _("end of file before option name start")); | |
171 | } while (c_isspace(c)); | |
172 | vb.reset(); | |
173 | if (!c_isalpha(c) && c != '_') | |
174 | badmethod(pathbuf,_("nonalpha where option name start wanted")); | |
175 | do { | |
176 | if (!c_isalnum(c) && c != '_') | |
177 | badmethod(pathbuf, _("non-alphanum in option name")); | |
178 | vb(c); | |
179 | c= fgetc(names); | |
180 | if (c == EOF) | |
181 | eofmethod(pathbuf, names, _("end of file in option name")); | |
182 | } while (!c_isspace(c)); | |
183 | opt->name= new char[strlen(vb.string())+1]; | |
184 | strcpy(opt->name,vb.string()); | |
185 | do { | |
186 | if (c == '\n') badmethod(pathbuf,_("newline before summary")); | |
187 | c= fgetc(names); | |
188 | if (c == EOF) | |
189 | eofmethod(pathbuf, names, _("end of file before summary")); | |
190 | } while (c_isspace(c)); | |
191 | vb.reset(); | |
192 | do { | |
193 | vb(c); | |
194 | c= fgetc(names); | |
195 | if (c == EOF) | |
196 | eofmethod(pathbuf, names, _("end of file in summary - missing newline")); | |
197 | } while (c != '\n'); | |
198 | opt->summary= new char[strlen(vb.string())+1]; | |
199 | strcpy(opt->summary,vb.string()); | |
200 | ||
201 | strcpy(pathinmeth,OPTIONSDESCPFX); | |
202 | strcpy(pathinmeth+sizeof(OPTIONSDESCPFX)-1,opt->name); | |
203 | descfile= fopen(pathbuf,"r"); | |
204 | if (!descfile) { | |
205 | if (errno != ENOENT) | |
206 | ohshite(_("unable to open option description file '%.250s'"), pathbuf); | |
207 | opt->description = nullptr; | |
208 | } else { /* descfile != 0 */ | |
209 | if (fstat(fileno(descfile),&stab)) | |
210 | ohshite(_("unable to stat option description file '%.250s'"), pathbuf); | |
211 | opt->description= new char[stab.st_size+1]; errno=0; | |
212 | size_t filelen = stab.st_size; | |
213 | if (fread(opt->description,1,stab.st_size+1,descfile) != filelen) | |
214 | ohshite(_("failed to read option description file '%.250s'"), pathbuf); | |
215 | opt->description[stab.st_size]= 0; | |
216 | if (ferror(descfile)) | |
217 | ohshite(_("error during read of option description file '%.250s'"), pathbuf); | |
218 | fclose(descfile); | |
219 | } | |
220 | strcpy(pathinmeth,METHODOPTIONSFILE); | |
221 | ||
222 | debug(dbg_general, | |
223 | " readmethods('%s',...) new option index='%s' name='%s'" | |
224 | " summary='%.20s' strlen(description=%s)=%zu method name='%s'" | |
225 | " path='%s' pathinmeth='%s'", | |
226 | pathbase, | |
227 | opt->index, opt->name, opt->summary, | |
228 | opt->description ? "'...'" : "null", | |
229 | opt->description ? strlen(opt->description) : -1, | |
230 | opt->meth->name, opt->meth->path, opt->meth->pathinmeth); | |
231 | ||
232 | dselect_option **optinsert = optionspp; | |
233 | while (*optinsert && strcmp(opt->index, (*optinsert)->index) > 0) | |
234 | optinsert = &(*optinsert)->next; | |
235 | opt->next= *optinsert; | |
236 | *optinsert= opt; | |
237 | (*nread)++; | |
238 | } | |
239 | if (ferror(names)) | |
240 | ohshite(_("error during read of method options file '%.250s'"), pathbuf); | |
241 | fclose(names); | |
242 | } | |
243 | closedir(dir); | |
244 | debug(dbg_general, "readmethods('%s',...) done", pathbase); | |
245 | delete[] pathbuf; | |
246 | } | |
247 | ||
248 | static char *methoptfile = nullptr; | |
249 | ||
250 | void getcurrentopt() { | |
251 | char methoptbuf[IMETHODMAXLEN+1+IOPTIONMAXLEN+2]; | |
252 | FILE *cmo; | |
253 | int l; | |
254 | char *p; | |
255 | ||
256 | if (methoptfile == nullptr) | |
257 | methoptfile = dpkg_db_get_path(CMETHOPTFILE); | |
258 | ||
259 | coption = nullptr; | |
260 | cmo= fopen(methoptfile,"r"); | |
261 | if (!cmo) { | |
262 | if (errno == ENOENT) return; | |
263 | ohshite(_("unable to open current option file '%.250s'"), methoptfile); | |
264 | } | |
265 | debug(dbg_general, "getcurrentopt() cmethopt open"); | |
266 | if (!fgets(methoptbuf,sizeof(methoptbuf),cmo)) { fclose(cmo); return; } | |
267 | if (fgetc(cmo) != EOF) { fclose(cmo); return; } | |
268 | if (!feof(cmo)) { fclose(cmo); return; } | |
269 | debug(dbg_general, "getcurrentopt() cmethopt eof"); | |
270 | fclose(cmo); | |
271 | debug(dbg_general, "getcurrentopt() cmethopt read"); | |
272 | l= strlen(methoptbuf); if (!l || methoptbuf[l-1] != '\n') return; | |
273 | methoptbuf[--l]= 0; | |
274 | debug(dbg_general, "getcurrentopt() cmethopt len and newline"); | |
275 | p= strchr(methoptbuf,' '); if (!p) return; | |
276 | debug(dbg_general, "getcurrentopt() cmethopt space"); | |
277 | *p++= 0; | |
278 | debug(dbg_general, "getcurrentopt() cmethopt meth name '%s'", methoptbuf); | |
279 | method *meth = methods; | |
280 | while (meth && strcmp(methoptbuf, meth->name)) | |
281 | meth = meth->next; | |
282 | if (!meth) return; | |
283 | debug(dbg_general, "getcurrentopt() cmethopt meth found; opt '%s'", p); | |
284 | dselect_option *opt = options; | |
285 | while (opt && (opt->meth != meth || strcmp(p, opt->name))) | |
286 | opt = opt->next; | |
287 | if (!opt) return; | |
288 | debug(dbg_general, "getcurrentopt() cmethopt opt found"); | |
289 | coption= opt; | |
290 | } | |
291 | ||
292 | void writecurrentopt() { | |
293 | struct atomic_file *file; | |
294 | ||
295 | assert(methoptfile); | |
296 | file = atomic_file_new(methoptfile, (enum atomic_file_flags)0); | |
297 | atomic_file_open(file); | |
298 | if (fprintf(file->fp, "%s %s\n", coption->meth->name, coption->name) == EOF) | |
299 | ohshite(_("unable to write new option to '%.250s'"), file->name_new); | |
300 | atomic_file_close(file); | |
301 | atomic_file_commit(file); | |
302 | atomic_file_free(file); | |
303 | } |