diff options
Diffstat (limited to 'winsup/utils')
-rw-r--r-- | winsup/utils/ChangeLog | 10 | ||||
-rw-r--r-- | winsup/utils/mkgroup.c | 162 | ||||
-rw-r--r-- | winsup/utils/mkpasswd.c | 145 | ||||
-rw-r--r-- | winsup/utils/utils.sgml | 41 |
4 files changed, 192 insertions, 166 deletions
diff --git a/winsup/utils/ChangeLog b/winsup/utils/ChangeLog index d3e8336f1..f014a2809 100644 --- a/winsup/utils/ChangeLog +++ b/winsup/utils/ChangeLog @@ -1,5 +1,15 @@ 2008-07-23 Corinna Vinschen <corinna@vinschen.de> + * mkgroup.c: Rework to allow per-domain/per-machine id_offset. + Add -b option to skip builtin groups. + (main): Simplify code. + * mkpasswd.c: Rework to allow per-domain/per-machine id_offset. + (main): Simplify code. + * utils.sgml: Explain changed mkgroup/mkpasswd -d/-D/-l/-L options. + Add mkgroup -b option. + +2008-07-23 Corinna Vinschen <corinna@vinschen.de> + * mkgroup.c: Consolidate variable names. (enum_unix_groups): New function. (print_special): Use LookupAccountSidW instead of LookupAccountSidA. diff --git a/winsup/utils/mkgroup.c b/winsup/utils/mkgroup.c index a68e8d5e0..b267b13ed 100644 --- a/winsup/utils/mkgroup.c +++ b/winsup/utils/mkgroup.c @@ -48,6 +48,8 @@ NET_API_STATUS WINAPI (*dsgetdcname)(LPWSTR,LPWSTR,GUID*,LPWSTR,ULONG,PDOMAIN_CO typedef struct { char *str; + DWORD id_offset; + BOOL domain; BOOL with_dom; } domlist_t; @@ -152,7 +154,7 @@ DBGSID builtin_sid_list[MAX_BUILTIN_SIDS]; DWORD builtin_sid_cnt; void -enum_unix_groups (domlist_t *dom_or_machine, const char *sep, int id_offset, +enum_unix_groups (domlist_t *dom_or_machine, const char *sep, DWORD id_offset, char *unix_grp_list) { WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; @@ -256,7 +258,7 @@ enum_unix_groups (domlist_t *dom_or_machine, const char *sep, int id_offset, int enum_local_groups (BOOL domain, domlist_t *dom_or_machine, const char *sep, - int id_offset, char *disp_groupname) + DWORD id_offset, char *disp_groupname, int print_builtin) { WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; PWCHAR servername = NULL; @@ -367,8 +369,10 @@ enum_local_groups (BOOL domain, domlist_t *dom_or_machine, const char *sep, { int b; + if (!print_builtin) + goto skip_group; is_builtin = TRUE; - if (servername && builtin_sid_cnt) + if (builtin_sid_cnt) for (b = 0; b < builtin_sid_cnt; b++) if (EqualSid (&builtin_sid_list[b], psid)) goto skip_group; @@ -380,8 +384,8 @@ enum_local_groups (BOOL domain, domlist_t *dom_or_machine, const char *sep, gid = *GetSidSubAuthority (psid, *GetSidSubAuthorityCount(psid) - 1); printf ("%ls%s%ls:%s:%ld:\n", - with_dom ? domain_name : L"", - with_dom ? sep : "", + with_dom && !is_builtin ? domain_name : L"", + with_dom && !is_builtin ? sep : "", buffer[i].lgrpi0_name, put_sid (psid), gid + (is_builtin ? 0 : id_offset)); @@ -399,7 +403,7 @@ skip_group: void enum_groups (BOOL domain, domlist_t *dom_or_machine, const char *sep, - int id_offset, char *disp_groupname) + DWORD id_offset, char *disp_groupname) { WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; PWCHAR servername = NULL; @@ -499,12 +503,12 @@ enum_groups (BOOL domain, domlist_t *dom_or_machine, const char *sep, continue; } } - printf ("%ls%s%ls:%s:%u:\n", + printf ("%ls%s%ls:%s:%lu:\n", with_dom ? domain_name : L"", with_dom ? sep : "", buffer[i].grpi2_name, put_sid (psid), - gid + id_offset); + id_offset + gid); } NetApiBufferFree (buffer); @@ -554,7 +558,7 @@ print_special (PSID_IDENTIFIER_AUTHORITY auth, BYTE cnt, } void -current_group (const char *sep, int id_offset) +current_group (const char *sep, DWORD id_offset) { DWORD len; HANDLE ptok; @@ -578,12 +582,12 @@ current_group (const char *sep, int id_offset) return; } gid = *GetSidSubAuthority (tg.psid, *GetSidSubAuthorityCount(tg.psid) - 1); - printf ("%ls%s%ls:%s:%u:\n", + printf ("%ls%s%ls:%s:%lu:\n", sep ? dom : L"", sep ?: "", grp, put_sid (tg.psid), - gid + id_offset); + id_offset + gid); } int @@ -594,12 +598,16 @@ usage (FILE * stream) "Print /etc/group file to stdout\n" "\n" "Options:\n" -" -l,--local [machine] print local groups (from local machine if no\n" -" machine specified)\n" -" -L,--Local [machine] ditto, but generate groupname with machine prefix\n" -" -d,--domain [domain] print domain groups (from current domain if no\n" -" domain specified)\n" -" -D,--Domain [domain] ditto, but generate groupname with machine prefix\n" +" -l,--local [machine[,offset]]\n" +" print local groups with gid offset offset\n" +" (from local machine if no machine specified)\n" +" -L,--Local [machine[,offset]]\n" +" ditto, but generate groupname with machine prefix\n" +" -d,--domain [domain[,offset]]\n" +" print domain groups with gid offset offset\n" +" (from current domain if no domain specified)\n" +" -D,--Domain [domain[,offset]]\n" +" ditto, but generate groupname with machine prefix\n" " -c,--current print current group\n" " -C,--Current ditto, but generate groupname with machine or\n" " domain prefix\n" @@ -609,6 +617,7 @@ usage (FILE * stream) " in domain or foreign server accounts.\n" " -g,--group groupname only return information for the specified group\n" " one of -l, -L, -d, -D must be specified, too\n" +" -b,--no-builtin don't print BUILTIN groups\n" " -U,--unix grouplist additionally print UNIX groups when using -l or -L\n" " on a UNIX Samba server\n" " grouplist is a comma-separated list of groupnames\n" @@ -625,6 +634,7 @@ usage (FILE * stream) } struct option longopts[] = { + {"no-builtin", no_argument, NULL, 'b'}, {"current", no_argument, NULL, 'c'}, {"Current", no_argument, NULL, 'C'}, {"domain", optional_argument, NULL, 'd'}, @@ -642,7 +652,7 @@ struct option longopts[] = { {0, no_argument, NULL, 0} }; -char opts[] = "cCd::D::g:hl::L::o:sS:uU:v"; +char opts[] = "bcCd::D::g:hl::L::o:sS:uU:v"; void print_version () @@ -693,17 +703,16 @@ fetch_primary_domain () int main (int argc, char **argv) { - int print_local = 0; - domlist_t locals[16]; - int print_domain = 0; - domlist_t domains[16]; - char *opt; + int print_domlist = 0; + domlist_t domlist[32]; + char *opt, *p, *ep; int print_current = 0; int print_system = 0; + int print_builtin = 1; char *print_unix = NULL; const char *sep_char = "\\"; - int id_offset = 10000; - int c, i, off; + DWORD id_offset = 10000, off; + int c, i; char *disp_groupname = NULL; BOOL in_domain; @@ -719,10 +728,11 @@ main (int argc, char **argv) if (in_domain) { if (!enum_local_groups (TRUE, NULL, sep_char, id_offset, - disp_groupname)) + disp_groupname, print_builtin)) enum_groups (TRUE, NULL, sep_char, id_offset, disp_groupname); } - else if (!enum_local_groups (FALSE, NULL, sep_char, 0, disp_groupname)) + else if (!enum_local_groups (FALSE, NULL, sep_char, 0, disp_groupname, + print_builtin)) enum_groups (FALSE, NULL, sep_char, 0, disp_groupname); return 0; } @@ -730,43 +740,41 @@ main (int argc, char **argv) while ((c = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) switch (c) { + case 'd': + case 'D': case 'l': case 'L': - if (print_local >= 16) + if (print_domlist >= 32) { - fprintf (stderr, "%s: Can not enumerate from more than 16 " - "servers.\n", __progname); + fprintf (stderr, "%s: Can not enumerate from more than 32 " + "domains and machines.\n", __progname); return 1; } opt = optarg ?: argv[optind] && argv[optind][0] != '-' ? argv[optind] : NULL; - for (i = 0; i < print_local; ++i) - if ((!locals[i].str && !opt) - || (locals[i].str && opt && !strcmp (locals[i].str, opt))) - goto skip_local; - if (!(locals[print_local].str = opt)) + for (i = 0; i < print_domlist; ++i) + if ((!domlist[i].str && !opt) + || (domlist[i].str && opt && !strcmp (domlist[i].str, opt))) + goto skip; + if (!(domlist[print_domlist].str = opt)) print_system = 1; - locals[print_local++].with_dom = c == 'L'; - skip_local: - break; - case 'd': - case 'D': - if (print_domain >= 16) + domlist[print_domlist].id_offset = ULONG_MAX; + if (opt && (p = strchr (opt, ','))) { - fprintf (stderr, "%s: Can not enumerate from more than 16 " - "domains.\n", __progname); - return 1; + if (p == opt + || !isdigit (p[1]) + || (domlist[print_domlist].id_offset = strtol (p + 1, &ep, 10) + , *ep)) + { + fprintf (stderr, "%s: Malformed machine,offset string '%s'. " + "Skipping...\n", __progname, opt); + break; + } + *p = '\0'; } - opt = optarg ?: - argv[optind] && argv[optind][0] != '-' ? argv[optind] : NULL; - for (i = 0; i < print_domain; ++i) - if ((!domains[i].str && !opt) - || (domains[i].str && opt && !strcmp (domains[i].str, opt))) - goto skip_domain; - if (!(domains[print_domain].str = opt)) - print_system = 1; - domains[print_domain++].with_dom = c == 'D'; - skip_domain: + domlist[print_domlist].domain = (c == 'd' || c == 'D'); + domlist[print_domlist++].with_dom = (c == 'D' || c == 'L'); + skip: break; case 'S': sep_char = optarg; @@ -795,6 +803,9 @@ main (int argc, char **argv) case 'o': id_offset = strtol (optarg, NULL, 10); break; + case 'b': + print_builtin = 0; + break; case 's': break; case 'u': @@ -813,44 +824,31 @@ main (int argc, char **argv) return 1; } - if (optind < argc - 1) - usage (stdout); - /* Get 'system' group */ - if (!disp_groupname && print_system) + if (!disp_groupname && print_system && print_builtin) print_special (&sid_nt_auth, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0); - off = 1; - for (i = 0; i < print_local; ++i) + off = id_offset; + for (i = 0; i < print_domlist; ++i) { - if (locals[i].str) + DWORD my_off = (domlist[i].domain || domlist[i].str) + ? domlist[i].id_offset != ULONG_MAX + ? domlist[i].id_offset : off : 0; + if (!enum_local_groups (domlist[i].domain, domlist + i, sep_char, + my_off, disp_groupname, print_builtin)) { - if (!enum_local_groups (FALSE, locals + i, sep_char, - id_offset * off, disp_groupname)) - { - if (print_unix) - enum_unix_groups (locals + i, sep_char, id_offset * off, - print_unix); - enum_groups (FALSE, locals + i, sep_char, id_offset * off++, - disp_groupname); - } + if (!domlist[i].domain && domlist[i].str && print_unix) + enum_unix_groups (domlist + i, sep_char, my_off, print_unix); + enum_groups (domlist[i].domain, domlist + i, sep_char, my_off, + disp_groupname); + if (my_off) + off += id_offset; } - else if (!enum_local_groups (FALSE, locals + i, sep_char, 0, - disp_groupname)) - enum_groups (FALSE, locals + i, sep_char, 0, disp_groupname); - } - - for (i = 0; i < print_domain; ++i) - { - if (!enum_local_groups (TRUE, domains + i, sep_char, id_offset * off, - disp_groupname)) - enum_groups (TRUE, domains + i, sep_char, id_offset * off++, - disp_groupname); } if (print_current) - current_group (sep_char, id_offset); + current_group (sep_char, off); return 0; } diff --git a/winsup/utils/mkpasswd.c b/winsup/utils/mkpasswd.c index 54f2d9414..19d2291a9 100644 --- a/winsup/utils/mkpasswd.c +++ b/winsup/utils/mkpasswd.c @@ -48,6 +48,8 @@ NET_API_STATUS WINAPI (*dsgetdcname)(LPWSTR,LPWSTR,GUID*,LPWSTR,ULONG,PDOMAIN_CO typedef struct { char *str; + DWORD id_offset; + BOOL domain; BOOL with_dom; } domlist_t; @@ -175,7 +177,7 @@ uni2ansi (LPWSTR wcs, char *mbs, int size) void current_user (int print_cygpath, const char *sep, const char *passed_home_path, - int id_offset, const char *disp_username) + DWORD id_offset, const char *disp_username) { DWORD len; HANDLE ptok; @@ -244,12 +246,12 @@ current_user (int print_cygpath, const char *sep, const char *passed_home_path, homedir_psx[PATH_MAX - 1] = '\0'; } - printf ("%ls%s%ls:unused:%u:%u:U-%ls\\%ls,%s:%s:/bin/bash\n", + printf ("%ls%s%ls:unused:%lu:%lu:U-%ls\\%ls,%s:%s:/bin/bash\n", sep ? dom : L"", sep ?: "", user, - uid + id_offset, - gid + id_offset, + id_offset + uid, + id_offset + gid, dom, user, put_sid (tu.psid), @@ -257,7 +259,7 @@ current_user (int print_cygpath, const char *sep, const char *passed_home_path, } void -enum_unix_users (domlist_t *dom_or_machine, const char *sep, int id_offset, +enum_unix_users (domlist_t *dom_or_machine, const char *sep, DWORD id_offset, char *unix_user_list) { WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; @@ -361,7 +363,7 @@ enum_unix_users (domlist_t *dom_or_machine, const char *sep, int id_offset, int enum_users (BOOL domain, domlist_t *dom_or_machine, const char *sep, - int print_cygpath, const char *passed_home_path, int id_offset, + int print_cygpath, const char *passed_home_path, DWORD id_offset, char *disp_username) { WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; @@ -486,12 +488,12 @@ enum_users (BOOL domain, domlist_t *dom_or_machine, const char *sep, } } - printf ("%ls%s%ls:unused:%u:%u:%ls%sU-%ls\\%ls,%s:%s:/bin/bash\n", + printf ("%ls%s%ls:unused:%lu:%lu:%ls%sU-%ls\\%ls,%s:%s:/bin/bash\n", with_dom ? domain_name : L"", with_dom ? sep : "", buffer[i].usri3_name, - uid + id_offset, - gid + id_offset, + id_offset + uid, + id_offset + gid, buffer[i].usri3_full_name ?: L"", buffer[i].usri3_full_name && buffer[i].usri3_full_name[0] ? "," : "", @@ -559,12 +561,16 @@ usage (FILE * stream) "Print /etc/passwd file to stdout\n" "\n" "Options:\n" -" -l,--local [machine] print local user accounts (from local machine\n" -" if no machine specified)\n" -" -L,--Local [machine] ditto, but generate username with machine prefix\n" -" -d,--domain [domain] print domain accounts (from current domain\n" -" if no domain specified)\n" -" -D,--Domain [domain] ditto, but generate username with domain prefix\n" +" -l,--local [machine[,offset]]\n" +" print local user accounts with uid offset offset\n" +" (from local machine if no machine specified)\n" +" -L,--Local [machine[,offset]]\n" +" ditto, but generate username with machine prefix\n" +" -d,--domain [domain[,offset]]\n" +" print domain accounts with uid offset offset\n" +" (from current domain if no domain specified)\n" +" -D,--Domain [domain[,offset]]\n" +" ditto, but generate username with domain prefix\n" " -c,--current print current user\n" " -C,--Current ditto, but generate username with machine or\n" " domain prefix\n" @@ -675,17 +681,15 @@ fetch_primary_domain () int main (int argc, char **argv) { - int print_local = 0; - domlist_t locals[16]; - int print_domain = 0; - domlist_t domains[16]; - char *opt; + int print_domlist = 0; + domlist_t domlist[32]; + char *opt, *p, *ep; int print_cygpath = 1; int print_current = 0; char *print_unix = NULL; const char *sep_char = "\\"; - int id_offset = 10000; - int c, i, off; + DWORD id_offset = 10000, off; + int c, i; char *disp_username = NULL; char passed_home_path[PATH_MAX]; BOOL in_domain; @@ -711,41 +715,40 @@ main (int argc, char **argv) while ((c = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) switch (c) { + case 'd': + case 'D': case 'l': case 'L': - if (print_local >= 16) + if (print_domlist >= 32) { - fprintf (stderr, "%s: Can not enumerate from more than 16 " - "servers.\n", __progname); + fprintf (stderr, "%s: Can not enumerate from more than 32 " + "domains and machines.\n", __progname); return 1; } opt = optarg ?: argv[optind] && argv[optind][0] != '-' ? argv[optind] : NULL; - for (i = 0; i < print_local; ++i) - if ((!locals[i].str && !opt) - || (locals[i].str && opt && !strcmp (locals[i].str, opt))) - goto skip_local; - locals[print_local].str = opt; - locals[print_local++].with_dom = c == 'L'; -skip_local: - break; - case 'd': - case 'D': - if (print_domain >= 16) + for (i = 0; i < print_domlist; ++i) + if ((!domlist[i].str && !opt) + || (domlist[i].str && opt && !strcmp (domlist[i].str, opt))) + goto skip; + domlist[print_domlist].str = opt; + domlist[print_domlist].id_offset = ULONG_MAX; + if (opt && (p = strchr (opt, ','))) { - fprintf (stderr, "%s: Can not enumerate from more than 16 " - "domains.\n", __progname); - return 1; + if (p == opt + || !isdigit (p[1]) + || (domlist[print_domlist].id_offset = strtol (p + 1, &ep, 10) + , *ep)) + { + fprintf (stderr, "%s: Malformed domain,offset string '%s'. " + "Skipping...\n", __progname, opt); + break; + } + *p = '\0'; } - opt = optarg ?: - argv[optind] && argv[optind][0] != '-' ? argv[optind] : NULL; - for (i = 0; i < print_domain; ++i) - if ((!domains[i].str && !opt) - || (domains[i].str && opt && !strcmp (domains[i].str, opt))) - goto skip_domain; - domains[print_domain].str = opt; - domains[print_domain++].with_dom = c == 'D'; -skip_domain: + domlist[print_domlist].domain = (c == 'd' || c == 'D'); + domlist[print_domlist++].with_dom = (c == 'D' || c == 'L'); +skip: break; case 'S': sep_char = optarg; @@ -772,7 +775,13 @@ skip_domain: print_current = 1; break; case 'o': - id_offset = strtol (optarg, NULL, 10); + id_offset = strtoul (optarg, &ep, 10); + if (*ep) + { + fprintf (stderr, "%s: Malformed offset '%s'. " + "Skipping...\n", __progname, optarg); + return 1; + } break; case 'g': break; @@ -806,33 +815,25 @@ skip_domain: return 1; } - if (optind < argc - 1) - usage (stdout); - - off = 1; - for (i = 0; i < print_local; ++i) + off = id_offset; + for (i = 0; i < print_domlist; ++i) { - if (locals[i].str) - { - if (print_unix) - enum_unix_users (locals + i, sep_char, id_offset * off, print_unix); - enum_users (FALSE, locals + i, sep_char, print_cygpath, - passed_home_path, id_offset * off++, disp_username); - } - else - { - enum_std_accounts (); - enum_users (FALSE, locals + i, sep_char, print_cygpath, - passed_home_path, 0, disp_username); - } + DWORD my_off = (domlist[i].domain || domlist[i].str) + ? domlist[i].id_offset != ULONG_MAX + ? domlist[i].id_offset : off : 0; + if (!domlist[i].domain && domlist[i].str && print_unix) + enum_unix_users (domlist + i, sep_char, my_off, print_unix); + if (!my_off) + enum_std_accounts (); + enum_users (domlist[i].domain, domlist + i, sep_char, print_cygpath, + passed_home_path, my_off, disp_username); + if (my_off) + off += id_offset; } - for (i = 0; i < print_domain; ++i) - enum_users (TRUE, domains + i, sep_char, print_cygpath, passed_home_path, - id_offset * off++, disp_username); - if (print_current) - current_user (print_cygpath, sep_char, passed_home_path, id_offset, disp_username); + current_user (print_cygpath, sep_char, passed_home_path, off, + disp_username); return 0; } diff --git a/winsup/utils/utils.sgml b/winsup/utils/utils.sgml index caacbd35c..dc52348fb 100644 --- a/winsup/utils/utils.sgml +++ b/winsup/utils/utils.sgml @@ -509,12 +509,16 @@ Usage: mkgroup [OPTION]... Print /etc/group file to stdout Options: - -l,--local [machine] print local groups (from local machine if no - machine specified) - -L,--Local [machine] ditto, but generate groupname with machine prefix - -d,--domain [domain] print domain groups (from current domain if no - domain specified) - -D,--Domain [domain] ditto, but generate groupname with machine prefix + -l,--local [machine[,offset]] + print local groups with gid offset offset + (from local machine if no machine specified) + -L,--Local [machine[,offset]] + ditto, but generate groupname with machine prefix + -d,--domain [domain[,offset]] + print domain groups with gid offset offset + (from current domain if no domain specified) + -D,--Domain [domain[,offset]] + ditto, but generate groupname with machine prefix -c,--current print current group -C,--Current ditto, but generate groupname with machine or domain prefix @@ -524,6 +528,7 @@ Options: in domain or foreign server accounts. -g,--group groupname only return information for the specified group one of -l, -L, -d, -D must be specified, too + -b,--no-builtin don't print BUILTIN groups -U,--unix grouplist additionally print UNIX groups when using -l or -L on a UNIX Samba server grouplist is a comma-separated list of groupnames @@ -561,6 +566,10 @@ allow you to specify where the information comes from, the local SAM of a machine or from the domain, or both. With the <literal>-d/-D</literal> options the program contacts a Domain Controller, which my be unreachable or have restricted access. +Comma-separated from the machine or domain, you can specify an offset +which is used as base added to the group's RID to compute the gid +(offset + RID = gid). This allows to create the same gids every time you +re-run <command>mkgroup</command>. For very simple needs, an entry for the current user's group can be created by using the option <literal>-c</literal> or <literal>-C</literal>. If you want to use one of the <literal>-D</literal>, <literal>-L</literal> @@ -595,12 +604,16 @@ Usage: mkpasswd [OPTIONS]... Print /etc/passwd file to stdout Options: - -l,--local [machine] print local user accounts (from local machine - if no machine specified) - -L,--Local [machine] ditto, but generate username with machine prefix - -d,--domain [domain] print domain accounts (from current domain - if no domain specified) - -D,--Domain [domain] ditto, but generate username with domain prefix + -l,--local [machine[,offset]] + print local user accounts with uid offset offset + (from local machine if no machine specified) + -L,--Local [machine[,offset]] + ditto, but generate username with machine prefix + -d,--domain [domain[,offset]] + print domain accounts with uid offset offset + (from current domain if no domain specified) + -D,--Domain [domain[,offset]] + ditto, but generate username with domain prefix -c,--current print current user -C,--Current ditto, but generate username with machine or domain prefix @@ -653,6 +666,10 @@ allow you to specify where the information comes from, the local machine or the domain (default or given), or both. With the <literal>-d/-D</literal> options the program contacts the Domain Controller, which may be unreachable or have restricted access. +Comma-separated from the machine or domain, you can specify an offset +which is used as base added to the user's RID to compute the uid +(offset + RID = uid). This allows to create the same uids every time you +re-run <command>mkpasswd</command>. An entry for the current user can be created by using the option <literal>-c</literal> or <literal>-C</literal>. If you want to use one of the <literal>-D</literal>, <literal>-L</literal> |