parse.c revision 99112
11590Srgrimes/* 21590Srgrimes * Copyright (c) 1989, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 3. All advertising materials mentioning features or use of this software 141590Srgrimes * must display the following acknowledgement: 151590Srgrimes * This product includes software developed by the University of 161590Srgrimes * California, Berkeley and its contributors. 171590Srgrimes * 4. Neither the name of the University nor the names of its contributors 181590Srgrimes * may be used to endorse or promote products derived from this software 191590Srgrimes * without specific prior written permission. 201590Srgrimes * 211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311590Srgrimes * SUCH DAMAGE. 321590Srgrimes */ 331590Srgrimes 341590Srgrimes#ifndef lint 3527315Scharnier#if 0 361590Srgrimesstatic char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/6/93"; 3727315Scharnier#endif 381590Srgrimes#endif /* not lint */ 3999112Sobrien#include <sys/cdefs.h> 4099112Sobrien__FBSDID("$FreeBSD: head/usr.bin/hexdump/parse.c 99112 2002-06-30 05:25:07Z obrien $"); 411590Srgrimes 421590Srgrimes#include <sys/types.h> 431590Srgrimes 4427315Scharnier#include <err.h> 451590Srgrimes#include <fcntl.h> 461590Srgrimes#include <stdio.h> 471590Srgrimes#include <stdlib.h> 481590Srgrimes#include <ctype.h> 491590Srgrimes#include <string.h> 501590Srgrimes#include "hexdump.h" 511590Srgrimes 521590SrgrimesFU *endfu; /* format at end-of-data */ 531590Srgrimes 541590Srgrimesvoid 551590Srgrimesaddfile(name) 561590Srgrimes char *name; 571590Srgrimes{ 5830921Sache register unsigned char *p; 591590Srgrimes FILE *fp; 601590Srgrimes int ch; 611590Srgrimes char buf[2048 + 1]; 621590Srgrimes 631590Srgrimes if ((fp = fopen(name, "r")) == NULL) 6427315Scharnier err(1, "%s", name); 651590Srgrimes while (fgets(buf, sizeof(buf), fp)) { 661590Srgrimes if (!(p = index(buf, '\n'))) { 6727315Scharnier warnx("line too long"); 681590Srgrimes while ((ch = getchar()) != '\n' && ch != EOF); 691590Srgrimes continue; 701590Srgrimes } 711590Srgrimes *p = '\0'; 721590Srgrimes for (p = buf; *p && isspace(*p); ++p); 731590Srgrimes if (!*p || *p == '#') 741590Srgrimes continue; 751590Srgrimes add(p); 761590Srgrimes } 771590Srgrimes (void)fclose(fp); 781590Srgrimes} 791590Srgrimes 801590Srgrimesvoid 811590Srgrimesadd(fmt) 8287203Smarkm const char *fmt; 831590Srgrimes{ 8487203Smarkm unsigned const char *p, *savep; 851590Srgrimes static FS **nextfs; 861590Srgrimes FS *tfs; 871590Srgrimes FU *tfu, **nextfu; 881590Srgrimes 891590Srgrimes /* start new linked list of format units */ 9080290Sobrien if ((tfs = calloc(1, sizeof(FS))) == NULL) 9180290Sobrien err(1, NULL); 921590Srgrimes if (!fshead) 931590Srgrimes fshead = tfs; 941590Srgrimes else 951590Srgrimes *nextfs = tfs; 961590Srgrimes nextfs = &tfs->nextfs; 971590Srgrimes nextfu = &tfs->nextfu; 981590Srgrimes 991590Srgrimes /* take the format string and break it up into format units */ 1001590Srgrimes for (p = fmt;;) { 1011590Srgrimes /* skip leading white space */ 1021590Srgrimes for (; isspace(*p); ++p); 1031590Srgrimes if (!*p) 1041590Srgrimes break; 1051590Srgrimes 1061590Srgrimes /* allocate a new format unit and link it in */ 10780290Sobrien if ((tfu = calloc(1, sizeof(FU))) == NULL) 10880290Sobrien err(1, NULL); 1091590Srgrimes *nextfu = tfu; 1101590Srgrimes nextfu = &tfu->nextfu; 1111590Srgrimes tfu->reps = 1; 1121590Srgrimes 1131590Srgrimes /* if leading digit, repetition count */ 1141590Srgrimes if (isdigit(*p)) { 1151590Srgrimes for (savep = p; isdigit(*p); ++p); 1161590Srgrimes if (!isspace(*p) && *p != '/') 1171590Srgrimes badfmt(fmt); 1181590Srgrimes /* may overwrite either white space or slash */ 1191590Srgrimes tfu->reps = atoi(savep); 1201590Srgrimes tfu->flags = F_SETREP; 1211590Srgrimes /* skip trailing white space */ 1221590Srgrimes for (++p; isspace(*p); ++p); 1231590Srgrimes } 1241590Srgrimes 1251590Srgrimes /* skip slash and trailing white space */ 1261590Srgrimes if (*p == '/') 1271590Srgrimes while (isspace(*++p)); 1281590Srgrimes 1291590Srgrimes /* byte count */ 1301590Srgrimes if (isdigit(*p)) { 1311590Srgrimes for (savep = p; isdigit(*p); ++p); 1321590Srgrimes if (!isspace(*p)) 1331590Srgrimes badfmt(fmt); 1341590Srgrimes tfu->bcnt = atoi(savep); 1351590Srgrimes /* skip trailing white space */ 1361590Srgrimes for (++p; isspace(*p); ++p); 1371590Srgrimes } 1381590Srgrimes 1391590Srgrimes /* format */ 1401590Srgrimes if (*p != '"') 1411590Srgrimes badfmt(fmt); 1421590Srgrimes for (savep = ++p; *p != '"';) 1431590Srgrimes if (*p++ == 0) 1441590Srgrimes badfmt(fmt); 1451590Srgrimes if (!(tfu->fmt = malloc(p - savep + 1))) 14680290Sobrien err(1, NULL); 1471590Srgrimes (void) strncpy(tfu->fmt, savep, p - savep); 1481590Srgrimes tfu->fmt[p - savep] = '\0'; 1491590Srgrimes escape(tfu->fmt); 1501590Srgrimes p++; 1511590Srgrimes } 1521590Srgrimes} 1531590Srgrimes 15491840Sobrienstatic const char *spec = ".#-+ 0123456789"; 1551590Srgrimes 1561590Srgrimesint 1571590Srgrimessize(fs) 1581590Srgrimes FS *fs; 1591590Srgrimes{ 1601590Srgrimes register FU *fu; 1611590Srgrimes register int bcnt, cursize; 16230921Sache register unsigned char *fmt; 1631590Srgrimes int prec; 1641590Srgrimes 1651590Srgrimes /* figure out the data block size needed for each format unit */ 1661590Srgrimes for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) { 1671590Srgrimes if (fu->bcnt) { 1681590Srgrimes cursize += fu->bcnt * fu->reps; 1691590Srgrimes continue; 1701590Srgrimes } 1711590Srgrimes for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) { 1721590Srgrimes if (*fmt != '%') 1731590Srgrimes continue; 1741590Srgrimes /* 1751590Srgrimes * skip any special chars -- save precision in 1761590Srgrimes * case it's a %s format. 1771590Srgrimes */ 1781590Srgrimes while (index(spec + 1, *++fmt)); 1791590Srgrimes if (*fmt == '.' && isdigit(*++fmt)) { 1801590Srgrimes prec = atoi(fmt); 1811590Srgrimes while (isdigit(*++fmt)); 1821590Srgrimes } 1831590Srgrimes switch(*fmt) { 1841590Srgrimes case 'c': 1851590Srgrimes bcnt += 1; 1861590Srgrimes break; 1871590Srgrimes case 'd': case 'i': case 'o': case 'u': 1881590Srgrimes case 'x': case 'X': 1891590Srgrimes bcnt += 4; 1901590Srgrimes break; 1911590Srgrimes case 'e': case 'E': case 'f': case 'g': case 'G': 1921590Srgrimes bcnt += 8; 1931590Srgrimes break; 1941590Srgrimes case 's': 1951590Srgrimes bcnt += prec; 1961590Srgrimes break; 1971590Srgrimes case '_': 1981590Srgrimes switch(*++fmt) { 1991590Srgrimes case 'c': case 'p': case 'u': 2001590Srgrimes bcnt += 1; 2011590Srgrimes break; 2021590Srgrimes } 2031590Srgrimes } 2041590Srgrimes } 2051590Srgrimes cursize += bcnt * fu->reps; 2061590Srgrimes } 2071590Srgrimes return (cursize); 2081590Srgrimes} 2091590Srgrimes 2101590Srgrimesvoid 2111590Srgrimesrewrite(fs) 2121590Srgrimes FS *fs; 2131590Srgrimes{ 2141590Srgrimes enum { NOTOKAY, USEBCNT, USEPREC } sokay; 2151590Srgrimes register PR *pr, **nextpr; 2161590Srgrimes register FU *fu; 21730921Sache unsigned char *p1, *p2, *fmtp; 21830921Sache char savech, cs[3]; 2191590Srgrimes int nconv, prec; 2201590Srgrimes 2211590Srgrimes for (fu = fs->nextfu; fu; fu = fu->nextfu) { 2221590Srgrimes /* 2231590Srgrimes * Break each format unit into print units; each conversion 2241590Srgrimes * character gets its own. 2251590Srgrimes */ 2261590Srgrimes for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) { 22780290Sobrien if ((pr = calloc(1, sizeof(PR))) == NULL) 22880290Sobrien err(1, NULL); 2291590Srgrimes if (!fu->nextpr) 2301590Srgrimes fu->nextpr = pr; 2311590Srgrimes else 2321590Srgrimes *nextpr = pr; 2331590Srgrimes 2341590Srgrimes /* Skip preceding text and up to the next % sign. */ 2351590Srgrimes for (p1 = fmtp; *p1 && *p1 != '%'; ++p1); 2361590Srgrimes 2371590Srgrimes /* Only text in the string. */ 2381590Srgrimes if (!*p1) { 2391590Srgrimes pr->fmt = fmtp; 2401590Srgrimes pr->flags = F_TEXT; 2411590Srgrimes break; 2421590Srgrimes } 2431590Srgrimes 2441590Srgrimes /* 2451590Srgrimes * Get precision for %s -- if have a byte count, don't 2461590Srgrimes * need it. 2471590Srgrimes */ 2481590Srgrimes if (fu->bcnt) { 2491590Srgrimes sokay = USEBCNT; 2501590Srgrimes /* Skip to conversion character. */ 2511590Srgrimes for (++p1; index(spec, *p1); ++p1); 2521590Srgrimes } else { 2531590Srgrimes /* Skip any special chars, field width. */ 2541590Srgrimes while (index(spec + 1, *++p1)); 2551590Srgrimes if (*p1 == '.' && isdigit(*++p1)) { 2561590Srgrimes sokay = USEPREC; 2571590Srgrimes prec = atoi(p1); 2581590Srgrimes while (isdigit(*++p1)); 2591590Srgrimes } else 2601590Srgrimes sokay = NOTOKAY; 2611590Srgrimes } 2621590Srgrimes 2631590Srgrimes p2 = p1 + 1; /* Set end pointer. */ 2641590Srgrimes cs[0] = *p1; /* Set conversion string. */ 2651590Srgrimes cs[1] = '\0'; 2661590Srgrimes 2671590Srgrimes /* 2681590Srgrimes * Figure out the byte count for each conversion; 2691590Srgrimes * rewrite the format as necessary, set up blank- 2701590Srgrimes * padding for end of data. 2711590Srgrimes */ 2721590Srgrimes switch(cs[0]) { 2731590Srgrimes case 'c': 2741590Srgrimes pr->flags = F_CHAR; 2751590Srgrimes switch(fu->bcnt) { 2761590Srgrimes case 0: case 1: 2771590Srgrimes pr->bcnt = 1; 2781590Srgrimes break; 2791590Srgrimes default: 2801590Srgrimes p1[1] = '\0'; 2811590Srgrimes badcnt(p1); 2821590Srgrimes } 2831590Srgrimes break; 2841590Srgrimes case 'd': case 'i': 2851590Srgrimes pr->flags = F_INT; 2861590Srgrimes goto isint; 2871590Srgrimes case 'o': case 'u': case 'x': case 'X': 2881590Srgrimes pr->flags = F_UINT; 2891590Srgrimesisint: cs[2] = '\0'; 2901590Srgrimes cs[1] = cs[0]; 2911590Srgrimes cs[0] = 'q'; 2921590Srgrimes switch(fu->bcnt) { 2931590Srgrimes case 0: case 4: 2941590Srgrimes pr->bcnt = 4; 2951590Srgrimes break; 2961590Srgrimes case 1: 2971590Srgrimes pr->bcnt = 1; 2981590Srgrimes break; 2991590Srgrimes case 2: 3001590Srgrimes pr->bcnt = 2; 3011590Srgrimes break; 3021590Srgrimes default: 3031590Srgrimes p1[1] = '\0'; 3041590Srgrimes badcnt(p1); 3051590Srgrimes } 3061590Srgrimes break; 3071590Srgrimes case 'e': case 'E': case 'f': case 'g': case 'G': 3081590Srgrimes pr->flags = F_DBL; 3091590Srgrimes switch(fu->bcnt) { 3101590Srgrimes case 0: case 8: 3111590Srgrimes pr->bcnt = 8; 3121590Srgrimes break; 3131590Srgrimes case 4: 3141590Srgrimes pr->bcnt = 4; 3151590Srgrimes break; 3161590Srgrimes default: 31796795Stjr if (fu->bcnt == sizeof(long double)) { 31896795Stjr cs[2] = '\0'; 31996795Stjr cs[1] = cs[0]; 32096795Stjr cs[0] = 'L'; 32196795Stjr pr->bcnt = sizeof(long double); 32296795Stjr } else { 32396795Stjr p1[1] = '\0'; 32496795Stjr badcnt(p1); 32596795Stjr } 3261590Srgrimes } 3271590Srgrimes break; 3281590Srgrimes case 's': 3291590Srgrimes pr->flags = F_STR; 3301590Srgrimes switch(sokay) { 3311590Srgrimes case NOTOKAY: 3321590Srgrimes badsfmt(); 3331590Srgrimes case USEBCNT: 3341590Srgrimes pr->bcnt = fu->bcnt; 3351590Srgrimes break; 3361590Srgrimes case USEPREC: 3371590Srgrimes pr->bcnt = prec; 3381590Srgrimes break; 3391590Srgrimes } 3401590Srgrimes break; 3411590Srgrimes case '_': 3421590Srgrimes ++p2; 3431590Srgrimes switch(p1[1]) { 3441590Srgrimes case 'A': 3451590Srgrimes endfu = fu; 3461590Srgrimes fu->flags |= F_IGNORE; 3471590Srgrimes /* FALLTHROUGH */ 3481590Srgrimes case 'a': 3491590Srgrimes pr->flags = F_ADDRESS; 3501590Srgrimes ++p2; 3511590Srgrimes switch(p1[2]) { 3521590Srgrimes case 'd': case 'o': case'x': 3531590Srgrimes cs[0] = 'q'; 3541590Srgrimes cs[1] = p1[2]; 3551590Srgrimes cs[2] = '\0'; 3561590Srgrimes break; 3571590Srgrimes default: 3581590Srgrimes p1[3] = '\0'; 3591590Srgrimes badconv(p1); 3601590Srgrimes } 3611590Srgrimes break; 3621590Srgrimes case 'c': 3631590Srgrimes pr->flags = F_C; 3641590Srgrimes /* cs[0] = 'c'; set in conv_c */ 3651590Srgrimes goto isint2; 3661590Srgrimes case 'p': 3671590Srgrimes pr->flags = F_P; 3681590Srgrimes cs[0] = 'c'; 3691590Srgrimes goto isint2; 3701590Srgrimes case 'u': 3711590Srgrimes pr->flags = F_U; 3721590Srgrimes /* cs[0] = 'c'; set in conv_u */ 3731590Srgrimesisint2: switch(fu->bcnt) { 3741590Srgrimes case 0: case 1: 3751590Srgrimes pr->bcnt = 1; 3761590Srgrimes break; 3771590Srgrimes default: 3781590Srgrimes p1[2] = '\0'; 3791590Srgrimes badcnt(p1); 3801590Srgrimes } 3811590Srgrimes break; 3821590Srgrimes default: 3831590Srgrimes p1[2] = '\0'; 3841590Srgrimes badconv(p1); 3851590Srgrimes } 3861590Srgrimes break; 3871590Srgrimes default: 3881590Srgrimes p1[1] = '\0'; 3891590Srgrimes badconv(p1); 3901590Srgrimes } 3911590Srgrimes 3921590Srgrimes /* 3931590Srgrimes * Copy to PR format string, set conversion character 3941590Srgrimes * pointer, update original. 3951590Srgrimes */ 3961590Srgrimes savech = *p2; 3971590Srgrimes p1[0] = '\0'; 39880290Sobrien if ((pr->fmt = calloc(1, strlen(fmtp) + 2)) == NULL) 39980290Sobrien err(1, NULL); 4001590Srgrimes (void)strcpy(pr->fmt, fmtp); 4011590Srgrimes (void)strcat(pr->fmt, cs); 4021590Srgrimes *p2 = savech; 4031590Srgrimes pr->cchar = pr->fmt + (p1 - fmtp); 4041590Srgrimes fmtp = p2; 4051590Srgrimes 4061590Srgrimes /* Only one conversion character if byte count. */ 4071590Srgrimes if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++) 40827315Scharnier errx(1, "byte count with multiple conversion characters"); 4091590Srgrimes } 4101590Srgrimes /* 4111590Srgrimes * If format unit byte count not specified, figure it out 4121590Srgrimes * so can adjust rep count later. 4131590Srgrimes */ 4141590Srgrimes if (!fu->bcnt) 4151590Srgrimes for (pr = fu->nextpr; pr; pr = pr->nextpr) 4161590Srgrimes fu->bcnt += pr->bcnt; 4171590Srgrimes } 4181590Srgrimes /* 4191590Srgrimes * If the format string interprets any data at all, and it's 4201590Srgrimes * not the same as the blocksize, and its last format unit 4211590Srgrimes * interprets any data at all, and has no iteration count, 4221590Srgrimes * repeat it as necessary. 4231590Srgrimes * 4241590Srgrimes * If, rep count is greater than 1, no trailing whitespace 4251590Srgrimes * gets output from the last iteration of the format unit. 4261590Srgrimes */ 42797329Stjr for (fu = fs->nextfu; fu; fu = fu->nextfu) { 4281590Srgrimes if (!fu->nextfu && fs->bcnt < blocksize && 4291590Srgrimes !(fu->flags&F_SETREP) && fu->bcnt) 4301590Srgrimes fu->reps += (blocksize - fs->bcnt) / fu->bcnt; 4311590Srgrimes if (fu->reps > 1) { 4321590Srgrimes for (pr = fu->nextpr;; pr = pr->nextpr) 4331590Srgrimes if (!pr->nextpr) 4341590Srgrimes break; 4351590Srgrimes for (p1 = pr->fmt, p2 = NULL; *p1; ++p1) 4361590Srgrimes p2 = isspace(*p1) ? p1 : NULL; 4371590Srgrimes if (p2) 4381590Srgrimes pr->nospace = p2; 4391590Srgrimes } 4401590Srgrimes } 4411590Srgrimes#ifdef DEBUG 4421590Srgrimes for (fu = fs->nextfu; fu; fu = fu->nextfu) { 4431590Srgrimes (void)printf("fmt:"); 4441590Srgrimes for (pr = fu->nextpr; pr; pr = pr->nextpr) 4451590Srgrimes (void)printf(" {%s}", pr->fmt); 4461590Srgrimes (void)printf("\n"); 4471590Srgrimes } 4481590Srgrimes#endif 4491590Srgrimes} 4501590Srgrimes 4511590Srgrimesvoid 4521590Srgrimesescape(p1) 4531590Srgrimes register char *p1; 4541590Srgrimes{ 4551590Srgrimes register char *p2; 4561590Srgrimes 4571590Srgrimes /* alphabetic escape sequences have to be done in place */ 4581590Srgrimes for (p2 = p1;; ++p1, ++p2) { 4591590Srgrimes if (!*p1) { 4601590Srgrimes *p2 = *p1; 4611590Srgrimes break; 4621590Srgrimes } 4631590Srgrimes if (*p1 == '\\') 4641590Srgrimes switch(*++p1) { 4651590Srgrimes case 'a': 4661590Srgrimes /* *p2 = '\a'; */ 4671590Srgrimes *p2 = '\007'; 4681590Srgrimes break; 4691590Srgrimes case 'b': 4701590Srgrimes *p2 = '\b'; 4711590Srgrimes break; 4721590Srgrimes case 'f': 4731590Srgrimes *p2 = '\f'; 4741590Srgrimes break; 4751590Srgrimes case 'n': 4761590Srgrimes *p2 = '\n'; 4771590Srgrimes break; 4781590Srgrimes case 'r': 4791590Srgrimes *p2 = '\r'; 4801590Srgrimes break; 4811590Srgrimes case 't': 4821590Srgrimes *p2 = '\t'; 4831590Srgrimes break; 4841590Srgrimes case 'v': 4851590Srgrimes *p2 = '\v'; 4861590Srgrimes break; 4871590Srgrimes default: 4881590Srgrimes *p2 = *p1; 4891590Srgrimes break; 4901590Srgrimes } 4911590Srgrimes } 4921590Srgrimes} 4931590Srgrimes 4941590Srgrimesvoid 4951590Srgrimesbadcnt(s) 4961590Srgrimes char *s; 4971590Srgrimes{ 49827315Scharnier errx(1, "%s: bad byte count", s); 4991590Srgrimes} 5001590Srgrimes 5011590Srgrimesvoid 5021590Srgrimesbadsfmt() 5031590Srgrimes{ 50427315Scharnier errx(1, "%%s: requires a precision or a byte count"); 5051590Srgrimes} 5061590Srgrimes 5071590Srgrimesvoid 5081590Srgrimesbadfmt(fmt) 50987203Smarkm const char *fmt; 5101590Srgrimes{ 51127315Scharnier errx(1, "\"%s\": bad format", fmt); 5121590Srgrimes} 5131590Srgrimes 5141590Srgrimesvoid 5151590Srgrimesbadconv(ch) 5161590Srgrimes char *ch; 5171590Srgrimes{ 51827315Scharnier errx(1, "%%%s: bad conversion character", ch); 5191590Srgrimes} 520