source: MondoRescue/branches/stable/mindi-busybox/changelog@ 821

Last change on this file since 821 was 821, checked in by Bruno Cornec, 18 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

File size: 20.1 KB
Line 
1Various bug fixes that apply to busybox 1.2.0, cherry-picked from the
2ongoing development branch. This will form the basis for busybox 1.2.1.
3
4I'll append fixes to this as they come up. (Check the file date, or the bug
5list below.) This file is basically a concatenation of the following:
6
7http://busybox.net/downloads/patches/svn-15575.patch
8http://busybox.net/downloads/patches/svn-15653.patch
9http://busybox.net/downloads/patches/svn-15656.patch
10http://busybox.net/downloads/patches/svn-15658.patch
11http://busybox.net/downloads/patches/svn-15659.patch
12http://busybox.net/downloads/patches/svn-15660.patch
13http://busybox.net/downloads/patches/svn-15670.patch
14http://busybox.net/downloads/patches/svn-15698.patch
15http://busybox.net/downloads/patches/svn-15700.patch
16http://busybox.net/downloads/patches/svn-15702.patch
17http://busybox.net/downloads/patches/svn-15705.patch
18http://busybox.net/downloads/patches/svn-15727.patch
19
20 ------------------------------------------------------------------------
21r15575 | landley | 2006-07-01 13:19:02 -0400 (Sat, 01 Jul 2006) | 2 lines
22Changed paths:
23 M /trunk/busybox/shell/lash.c
24
25Patch from Shaun Jackman moving the var=value logic to where it can do some
26good.
27
28 ------------------------------------------------------------------------
29Index: shell/lash.c
30===================================================================
31--- shell/lash.c (revision 15574)
32+++ shell/lash.c (revision 15575)
33@@ -1171,12 +1171,6 @@
34 {
35 struct built_in_command *x;
36
37- /* Check if the command sets an environment variable. */
38- if( strchr(child->argv[0], '=') != NULL ) {
39- child->argv[1] = child->argv[0];
40- _exit(builtin_export(child));
41- }
42-
43 /* Check if the command matches any of the non-forking builtins.
44 * Depending on context, this might be redundant. But it's
45 * easier to waste a few CPU cycles than it is to figure out
46@@ -1300,6 +1294,12 @@
47 * is doomed to failure, and doesn't work on bash, either.
48 */
49 if (newjob->num_progs == 1) {
50+ /* Check if the command sets an environment variable. */
51+ if (strchr(child->argv[0], '=') != NULL) {
52+ child->argv[1] = child->argv[0];
53+ return builtin_export(child);
54+ }
55+
56 for (x = bltins; x->cmd; x++) {
57 if (strcmp(child->argv[0], x->cmd) == 0 ) {
58 int rcode;
59 ------------------------------------------------------------------------
60r15653 | landley | 2006-07-05 21:09:21 -0400 (Wed, 05 Jul 2006) | 6 lines
61Changed paths:
62 M /trunk/busybox/shell/ash.c
63
64Bug fix from Vladimir Oleynic via Paul Fox for:
65echo "+bond0" > /sys/class/net/bonding_masters
66while true; do
67 echo hello
68done
69
70 ------------------------------------------------------------------------
71Index: shell/ash.c
72===================================================================
73--- shell/ash.c (revision 15652)
74+++ shell/ash.c (revision 15653)
75@@ -3469,6 +3469,7 @@
76 flushall();
77 cmddone:
78 exitstatus |= ferror(stdout);
79+ clearerr(stdout);
80 commandname = savecmdname;
81 exsig = 0;
82 handler = savehandler;
83 ------------------------------------------------------------------------
84r15656 | landley | 2006-07-06 12:41:56 -0400 (Thu, 06 Jul 2006) | 5 lines
85Changed paths:
86 M /trunk/busybox/util-linux/dmesg.c
87
88Fix three embarassing thinkos in the new dmesg.c:
891) the c argument shouldn't have had a : after that, dunno how that got there.
902) the xgetlarg for level was using size
913) because xgetlarg's error message _SUCKS_ (it does a show_usage() rather than giving any specific info about the range that was violated) I dropped the range down to 2 bytes. (Which works fine, I dunno why we were nit-picking about that...)
92
93 ------------------------------------------------------------------------
94Index: util-linux/dmesg.c
95===================================================================
96--- util-linux/dmesg.c (revision 15655)
97+++ util-linux/dmesg.c (revision 15656)
98@@ -15,16 +15,16 @@
99 int dmesg_main(int argc, char *argv[])
100 {
101 char *size, *level;
102- int flags = bb_getopt_ulflags(argc, argv, "c:s:n:", &size, &level);
103+ int flags = bb_getopt_ulflags(argc, argv, "cs:n:", &size, &level);
104
105 if (flags & 4) {
106- if(klogctl(8, NULL, bb_xgetlarg(size, 10, 0, 10)))
107+ if(klogctl(8, NULL, bb_xgetlarg(level, 10, 0, 10)))
108 bb_perror_msg_and_die("klogctl");
109 } else {
110 int len;
111 char *buf;
112
113- len = (flags & 2) ? bb_xgetlarg(size, 10, 4096, INT_MAX) : 16384;
114+ len = (flags & 2) ? bb_xgetlarg(size, 10, 2, INT_MAX) : 16384;
115 buf = xmalloc(len);
116 if (0 > (len = klogctl(3 + (flags & 1), buf, len)))
117 bb_perror_msg_and_die("klogctl");
118 ------------------------------------------------------------------------
119r15658 | pgf | 2006-07-06 16:00:43 -0400 (Thu, 06 Jul 2006) | 4 lines
120Changed paths:
121 M /trunk/busybox/Makefile
122
123fix dependencies so that compressed usage gets rebuilt if
124 a) include/usage.h is changed, and
125 b) after "make clean".
126
127 ------------------------------------------------------------------------
128Index: Makefile
129===================================================================
130--- Makefile (revision 15657)
131+++ Makefile (revision 15658)
132@@ -436,13 +436,16 @@
133
134 ifeq ($(strip $(CONFIG_FEATURE_COMPRESS_USAGE)),y)
135 USAGE_BIN:=scripts/usage
136-$(USAGE_BIN): $(top_srcdir)/scripts/usage.c .config
137+$(USAGE_BIN): $(top_srcdir)/scripts/usage.c .config \
138+ $(top_srcdir)/include/usage.h
139 $(do_link.h)
140
141 DEP_INCLUDES += include/usage_compressed.h
142
143-include/usage_compressed.h: .config $(USAGE_BIN) $(top_srcdir)/scripts/usage_compressed
144- $(Q)SED="$(SED)" $(SHELL) $(top_srcdir)/scripts/usage_compressed "$(top_builddir)/scripts" > $@
145+include/usage_compressed.h: .config $(USAGE_BIN) \
146+ $(top_srcdir)/scripts/usage_compressed
147+ $(Q)SED="$(SED)" $(SHELL) $(top_srcdir)/scripts/usage_compressed \
148+ "$(top_builddir)/scripts" > $@
149 endif # CONFIG_FEATURE_COMPRESS_USAGE
150
151 # workaround alleged bug in make-3.80, make-3.81
152@@ -470,7 +473,8 @@
153 docs/BusyBox.txt docs/BusyBox.1 docs/BusyBox.html \
154 docs/busybox.net/BusyBox.html busybox.links \
155 libbusybox.so* \
156- .config.old busybox busybox_unstripped
157+ .config.old busybox busybox_unstripped \
158+ include/usage_compressed.h scripts/usage
159 - rm -r -f _install testsuite/links
160 - find . -name .\*.flags -o -name \*.o -o -name \*.om -o -name \*.syn \
161 -o -name \*.os -o -name \*.osm -o -name \*.a | xargs rm -f
162 ------------------------------------------------------------------------
163r15659 | landley | 2006-07-06 16:02:47 -0400 (Thu, 06 Jul 2006) | 3 lines
164Changed paths:
165 M /trunk/busybox/libbb/Makefile
166
167Attempt to address Shaun Jackman's problem adding "busybox: busybox.bflt" to
168.config.mak.
169
170 ------------------------------------------------------------------------
171Index: libbb/Makefile
172===================================================================
173--- libbb/Makefile (revision 15658)
174+++ libbb/Makefile (revision 15659)
175@@ -12,6 +12,12 @@
176 endif
177 srcdir=$(top_srcdir)/libbb
178 LIBBB_DIR:=./
179+
180+# Ensure "all" is still the default target when make is run by itself in
181+# libbb, even if the files we include define rules for targets.
182+
183+all:
184+
185 include $(top_srcdir)/Rules.mak
186 include $(top_builddir)/.config
187 include Makefile.in
188 ------------------------------------------------------------------------
189r15660 | landley | 2006-07-06 16:30:19 -0400 (Thu, 06 Jul 2006) | 3 lines
190Changed paths:
191 M /trunk/busybox/archival/libunarchive/get_header_tar.c
192
193Fix tar so it can extract git-generated tarballs, based on a suggestion
194from Erik Frederiksen.
195
196 ------------------------------------------------------------------------
197Index: archival/libunarchive/get_header_tar.c
198===================================================================
199--- archival/libunarchive/get_header_tar.c (revision 15659)
200+++ archival/libunarchive/get_header_tar.c (revision 15660)
201@@ -137,10 +137,6 @@
202 case '1':
203 file_header->mode |= S_IFREG;
204 break;
205- case 'x':
206- case 'g':
207- bb_error_msg_and_die("pax is not tar");
208- break;
209 case '7':
210 /* Reserved for high performance files, treat as normal file */
211 case 0:
212@@ -188,8 +184,11 @@
213 case 'N': /* Old GNU for names > 100 characters */
214 case 'S': /* Sparse file */
215 case 'V': /* Volume header */
216- bb_error_msg("Ignoring GNU extension type %c", tar.formated.typeflag);
217 #endif
218+ case 'g': /* pax global header */
219+ case 'x': /* pax extended header */
220+ bb_error_msg("Ignoring extension type %c", tar.formated.typeflag);
221+ break;
222 default:
223 bb_error_msg("Unknown typeflag: 0x%x", tar.formated.typeflag);
224 }
225 ------------------------------------------------------------------------
226r15670 | landley | 2006-07-09 13:03:07 -0400 (Sun, 09 Jul 2006) | 3 lines
227Changed paths:
228 M /trunk/busybox/shell/lash.c
229
230Bugfix from Shaun Jackman (check that argv[optind] isn't null before
231dereferencing it) plus a bunch of tweaks from me.
232
233 ------------------------------------------------------------------------
234Index: shell/lash.c
235===================================================================
236--- shell/lash.c (revision 15669)
237+++ shell/lash.c (revision 15670)
238@@ -1498,6 +1498,8 @@
239 remove_job(&job_list, job_list.fg);
240 }
241 }
242+#else
243+void free_memory(void);
244 #endif
245
246 #ifdef CONFIG_LASH_JOB_CONTROL
247@@ -1528,7 +1530,7 @@
248 /* Put ourselves in our own process group. */
249 setsid();
250 shell_pgrp = getpid ();
251- setpgid (shell_pgrp, shell_pgrp);
252+ setpgid(shell_pgrp, shell_pgrp);
253
254 /* Grab control of the terminal. */
255 tcsetpgrp(shell_terminal, shell_pgrp);
256@@ -1577,7 +1579,7 @@
257 argv = argv+optind;
258 break;
259 case 'i':
260- interactive = TRUE;
261+ interactive++;
262 break;
263 default:
264 bb_show_usage();
265@@ -1591,18 +1593,18 @@
266 * standard output is a terminal
267 * Refer to Posix.2, the description of the `sh' utility. */
268 if (argv[optind]==NULL && input==stdin &&
269- isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
270- interactive=TRUE;
271+ isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
272+ {
273+ interactive++;
274 }
275 setup_job_control();
276- if (interactive==TRUE) {
277- //printf( "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
278+ if (interactive) {
279 /* Looks like they want an interactive shell */
280-#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
281- printf( "\n\n%s Built-in shell (lash)\n", BB_BANNER);
282- printf( "Enter 'help' for a list of built-in commands.\n\n");
283-#endif
284- } else if (local_pending_command==NULL) {
285+ if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
286+ printf( "\n\n%s Built-in shell (lash)\n", BB_BANNER);
287+ printf( "Enter 'help' for a list of built-in commands.\n\n");
288+ }
289+ } else if (!local_pending_command && argv[optind]) {
290 //printf( "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
291 input = bb_xfopen(argv[optind], "r");
292 /* be lazy, never mark this closed */
293@@ -1614,15 +1616,10 @@
294 if (!cwd)
295 cwd = bb_msg_unknown;
296
297-#ifdef CONFIG_FEATURE_CLEAN_UP
298- atexit(free_memory);
299-#endif
300+ if (ENABLE_FEATURE_CLEAN_UP) atexit(free_memory);
301
302-#ifdef CONFIG_FEATURE_COMMAND_EDITING
303- cmdedit_set_initial_prompt();
304-#else
305- PS1 = NULL;
306-#endif
307+ if (ENABLE_FEATURE_COMMAND_EDITING) cmdedit_set_initial_prompt();
308+ else PS1 = NULL;
309
310 return (busy_loop(input));
311 }
312 ------------------------------------------------------------------------
313r15698 | vapier | 2006-07-14 23:59:00 -0400 (Fri, 14 Jul 2006) | 2 lines
314Changed paths:
315 M /trunk/busybox/libbb/obscure.c
316
317Tito writes: If the gecos field of an user is empty, obscure reports a false "similar to gecos" error.
318
319 ------------------------------------------------------------------------
320Index: libbb/obscure.c
321===================================================================
322--- libbb/obscure.c (revision 15697)
323+++ libbb/obscure.c (revision 15698)
324@@ -109,7 +109,7 @@
325 return "similar to username";
326 }
327 /* no gecos as-is, as sub-string, reversed, capitalized, doubled */
328- if (string_checker(new_p, pw->pw_gecos)) {
329+ if (*pw->pw_gecos && string_checker(new_p, pw->pw_gecos)) {
330 return "similar to gecos";
331 }
332 /* hostname as-is, as sub-string, reversed, capitalized, doubled */
333 ------------------------------------------------------------------------
334r15700 | landley | 2006-07-15 19:00:46 -0400 (Sat, 15 Jul 2006) | 4 lines
335Changed paths:
336 M /trunk/busybox/include/libbb.h
337 M /trunk/busybox/libbb/xfuncs.c
338
339We need xsetuid() and xsetgid() because per-user process resource limits can
340prevent a process from switching to a user that has too many processes, and
341when that happens WE'RE STILL ROOT. See http://lwn.net/Articles/190331/
342
343 ------------------------------------------------------------------------
344Index: libbb/xfuncs.c
345===================================================================
346--- libbb/xfuncs.c (revision 15699)
347+++ libbb/xfuncs.c (revision 15700)
348@@ -232,3 +232,15 @@
349 return 0;
350 }
351 #endif
352+
353+#ifdef L_setuid
354+void xsetgid(gid_t gid)
355+{
356+ if (setgid(gid)) bb_error_msg_and_die("setgid");
357+}
358+
359+void xsetuid(uid_t uid)
360+{
361+ if (setuid(uid)) bb_error_msg_and_die("setuid");
362+}
363+#endif
364Index: include/libbb.h
365===================================================================
366--- include/libbb.h (revision 15699)
367+++ include/libbb.h (revision 15700)
368@@ -185,6 +185,8 @@
369 extern bb_xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
370 extern void bb_xlisten(int s, int backlog);
371 extern void bb_xchdir(const char *path);
372+extern void xsetgid(gid_t gid);
373+extern void xsetuid(uid_t uid);
374
375 #define BB_GETOPT_ERROR 0x80000000UL
376 extern const char *bb_opt_complementally;
377 ------------------------------------------------------------------------
378r15702 | landley | 2006-07-16 04:06:34 -0400 (Sun, 16 Jul 2006) | 2 lines
379Changed paths:
380 M /trunk/busybox/loginutils/passwd.c
381 M /trunk/busybox/networking/arping.c
382 M /trunk/busybox/networking/ether-wake.c
383 M /trunk/busybox/networking/fakeidentd.c
384 M /trunk/busybox/networking/inetd.c
385 M /trunk/busybox/networking/traceroute.c
386
387Convert setuid/setgid users to xsetuid/xsetgid.
388
389 ------------------------------------------------------------------------
390Index: networking/fakeidentd.c
391===================================================================
392--- networking/fakeidentd.c (revision 15701)
393+++ networking/fakeidentd.c (revision 15702)
394@@ -159,8 +159,8 @@
395
396 close(0);
397 inetbind();
398- if (setgid(nogrp)) bb_error_msg_and_die("Could not setgid()");
399- if (setuid(nobody)) bb_error_msg_and_die("Could not setuid()");
400+ xsetgid(nogrp);
401+ xsetuid(nobody);
402 close(1);
403 close(2);
404
405Index: networking/ether-wake.c
406===================================================================
407--- networking/ether-wake.c (revision 15701)
408+++ networking/ether-wake.c (revision 15702)
409@@ -145,7 +145,7 @@
410 s = make_socket();
411
412 /* now that we have a raw socket we can drop root */
413- setuid(getuid());
414+ xsetuid(getuid());
415
416 /* look up the dest mac address */
417 get_dest_addr(argv[optind], &eaddr);
418Index: networking/inetd.c
419===================================================================
420--- networking/inetd.c (revision 15701)
421+++ networking/inetd.c (revision 15702)
422@@ -1513,11 +1513,11 @@
423 if (sep->se_group) {
424 pwd->pw_gid = grp->gr_gid;
425 }
426- setgid ((gid_t) pwd->pw_gid);
427+ xsetgid ((gid_t) pwd->pw_gid);
428 initgroups (pwd->pw_name, pwd->pw_gid);
429- setuid ((uid_t) pwd->pw_uid);
430+ xsetuid((uid_t) pwd->pw_uid);
431 } else if (sep->se_group) {
432- setgid (grp->gr_gid);
433+ xsetgid(grp->gr_gid);
434 setgroups (1, &grp->gr_gid);
435 }
436 dup2 (ctrl, 0);
437Index: networking/traceroute.c
438===================================================================
439--- networking/traceroute.c (revision 15701)
440+++ networking/traceroute.c (revision 15702)
441@@ -941,7 +941,6 @@
442 #endif
443 u_short off = 0;
444 struct IFADDRLIST *al;
445- int uid = getuid();
446 char *device = NULL;
447 int max_ttl = 30;
448 char *max_ttl_str = NULL;
449@@ -1010,8 +1009,7 @@
450 * set the ip source address of the outbound
451 * probe (e.g., on a multi-homed host).
452 */
453- if (uid)
454- bb_error_msg_and_die("-s %s: Permission denied", source);
455+ if (getuid()) bb_error_msg_and_die("-s %s: Permission denied", source);
456 }
457 if(waittime_str)
458 waittime = str2val(waittime_str, "wait time", 2, 24 * 60 * 60);
459@@ -1160,8 +1158,8 @@
460 sizeof(on));
461
462 /* Revert to non-privileged user after opening sockets */
463- setgid(getgid());
464- setuid(uid);
465+ xsetgid(getgid());
466+ xsetuid(getuid());
467
468 outip = (struct ip *)xcalloc(1, (unsigned)packlen);
469
470Index: networking/arping.c
471===================================================================
472--- networking/arping.c (revision 15701)
473+++ networking/arping.c (revision 15702)
474@@ -262,7 +262,8 @@
475 s = socket(PF_PACKET, SOCK_DGRAM, 0);
476 ifindex = errno;
477
478- setuid(getuid());
479+ // Drop suid root privileges
480+ xsetuid(getuid());
481
482 {
483 unsigned long opt;
484Index: loginutils/passwd.c
485===================================================================
486--- loginutils/passwd.c (revision 15701)
487+++ loginutils/passwd.c (revision 15702)
488@@ -227,10 +227,7 @@
489 signal(SIGINT, SIG_IGN);
490 signal(SIGQUIT, SIG_IGN);
491 umask(077);
492- if (setuid(0)) {
493- syslog(LOG_ERR, "can't setuid(0)");
494- bb_error_msg_and_die( "Cannot change ID to root.\n");
495- }
496+ xsetuid(0);
497 if (!update_passwd(pw, crypt_passwd)) {
498 syslog(LOG_INFO, "password for `%s' changed by user `%s'", name,
499 myname);
500 ------------------------------------------------------------------------
501r15705 | landley | 2006-07-16 14:58:18 -0400 (Sun, 16 Jul 2006) | 2 lines
502Changed paths:
503 M /trunk/busybox/loginutils/adduser.c
504
505Bugfix from Tito to make sure /etc/group gets updated.
506
507 ------------------------------------------------------------------------
508Index: loginutils/adduser.c
509===================================================================
510--- loginutils/adduser.c (revision 15704)
511+++ loginutils/adduser.c (revision 15705)
512@@ -96,6 +96,7 @@
513 static int adduser(struct passwd *p, unsigned long flags)
514 {
515 FILE *file;
516+ int addgroup = !p->pw_gid;
517
518 /* make sure everything is kosher and setup uid && gid */
519 file = bb_xfopen(bb_path_passwd_file, "a");
520@@ -132,9 +133,8 @@
521 /* add to group */
522 /* addgroup should be responsible for dealing w/ gshadow */
523 /* if using a pre-existing group, don't create one */
524- if (p->pw_gid == 0) {
525- addgroup_wrapper(p);
526- }
527+ if (addgroup) addgroup_wrapper(p);
528+
529 /* Clear the umask for this process so it doesn't
530 * * screw up the permissions on the mkdir and chown. */
531 umask(0);
532 ------------------------------------------------------------------------
533r15727 | landley | 2006-07-19 17:33:42 -0400 (Wed, 19 Jul 2006) | 4 lines
534Changed paths:
535 M /trunk/busybox/modutils/modprobe.c
536
537Patch from Yann Morin to look for modules.conf in the right place on 2.6.
538Fixes http://bugs.busybox.net/view.php?id=942
539
540
541 ------------------------------------------------------------------------
542Index: modutils/modprobe.c
543===================================================================
544--- modutils/modprobe.c (revision 15726)
545+++ modutils/modprobe.c (revision 15727)
546@@ -545,29 +545,37 @@
547 }
548 close ( fd );
549
550+ /*
551+ * First parse system-specific options and aliases
552+ * as they take precedence over the kernel ones.
553+ */
554 if (!ENABLE_FEATURE_2_6_MODULES
555 || ( fd = open ( "/etc/modprobe.conf", O_RDONLY )) < 0 )
556 if (( fd = open ( "/etc/modules.conf", O_RDONLY )) < 0 )
557- if (( fd = open ( "/etc/conf.modules", O_RDONLY )) < 0 )
558- return first;
559+ fd = open ( "/etc/conf.modules", O_RDONLY );
560
561- include_conf (&first, &current, buffer, sizeof(buffer), fd);
562- close(fd);
563+ if (fd >= 0) {
564+ include_conf (&first, &current, buffer, sizeof(buffer), fd);
565+ close(fd);
566+ }
567
568- filename = bb_xasprintf("/lib/modules/%s/modules.alias", un.release);
569- fd = open ( filename, O_RDONLY );
570- if (ENABLE_FEATURE_CLEAN_UP)
571- free(filename);
572- if (fd < 0) {
573- /* Ok, that didn't work. Fall back to looking in /lib/modules */
574- if (( fd = open ( "/lib/modules/modules.alias", O_RDONLY )) < 0 ) {
575- return first;
576+ /* Only 2.6 has a modules.alias file */
577+ if (ENABLE_FEATURE_2_6_MODULES) {
578+ /* Parse kernel-declared aliases */
579+ filename = bb_xasprintf("/lib/modules/%s/modules.alias", un.release);
580+ if ((fd = open ( filename, O_RDONLY )) < 0) {
581+ /* Ok, that didn't work. Fall back to looking in /lib/modules */
582+ fd = open ( "/lib/modules/modules.alias", O_RDONLY );
583 }
584+ if (ENABLE_FEATURE_CLEAN_UP)
585+ free(filename);
586+
587+ if (fd >= 0) {
588+ include_conf (&first, &current, buffer, sizeof(buffer), fd);
589+ close(fd);
590+ }
591 }
592
593- include_conf (&first, &current, buffer, sizeof(buffer), fd);
594- close(fd);
595-
596 return first;
597 }
598
Note: See TracBrowser for help on using the repository browser.