/* © 2000 Compaq Computer Corporation COMPAQ Registered in U.S. Patent and Trademark Office. Confidential computer software. Valid license from Compaq required for possession, use or copying. Consistent with FAR 12.211 and 12.212, Commercial Computer Software, Computer Software Documentation, and Technical Data for Commercial Items are licensed to the U.S. Government under vendor's standard commercial license. */ #include #include #include #include #include #include #ifdef VMS #include #else #include #endif #include "dsimple.h" #define MAXSTR 10000 #define min(a,b) ((a) < (b) ? (a) : (b)) /* * * The Thunk Manager - routines to create, add to, and free thunk lists * */ typedef struct { int thunk_count; long value; char *extra_value; char *format; char *dformat; } thunk; thunk *Create_Thunk_List() { thunk *tptr; tptr = (thunk *) Malloc( sizeof(thunk) ); tptr->thunk_count = 0; return(tptr); } Free_Thunk_List(list) thunk *list; { free(list); } thunk *Add_Thunk(list, t) thunk *list; thunk t; { int i; i = list->thunk_count; list = (thunk *) realloc(list, (i+1)*sizeof(thunk) ); if (!list) Fatal_Error("Out of memory!"); list[i++] = t; list->thunk_count = i; return(list); } /* * Misc. routines */ char *Copy_String(string) char *string; { char *new; int length; length = strlen(string) + 1; new = (char *) Malloc(length); #ifdef VMS memcpy(new, new, length); #else bcopy(string, new, length); #endif return(new); } int Read_Char(stream) FILE *stream; { int c; c = getc(stream); if (c==EOF) Fatal_Error("Bad format file: Unexpected EOF."); return(c); } Read_White_Space(stream) FILE *stream; { int c; while ((c=getc(stream))==' ' || c=='\n' || c=='\t'); ungetc(c, stream); } static char _large_buffer[MAXSTR+10]; char *Read_Quoted(stream) FILE *stream; { char *ptr; int c, length; Read_White_Space(stream); if (Read_Char(stream)!='\'') Fatal_Error("Bad format file format: missing dformat."); ptr = _large_buffer; length=MAXSTR; for (;;) { if (length<0) Fatal_Error("Bad format file format: dformat too long."); c = Read_Char(stream); if (c==(int) '\'') break; ptr++[0]=c; length--; if (c== (int) '\\') { c=Read_Char(stream); if (c=='\n') { ptr--; length++; } else ptr++[0]=c; length--; } } ptr++[0]='\0'; return(Copy_String(_large_buffer)); } /* * * Atom to format, dformat mapping Manager * */ #define D_FORMAT "0x" /* Default format for properties */ #define D_DFORMAT " = $0+\n" /* Default display pattern for properties */ static thunk *_property_formats = 0; /* Holds mapping */ Apply_Default_Formats(format, dformat) char **format; char **dformat; { if (!*format) *format = D_FORMAT; if (!*dformat) *dformat = D_DFORMAT; } Lookup_Formats(atom, format, dformat) Atom atom; char **format; char **dformat; { int i; if (_property_formats) for (i=_property_formats->thunk_count-1; i>=0; i--) if (_property_formats[i].value==atom) { if (!*format) *format = _property_formats[i].format; if (!*dformat) *dformat = _property_formats[i].dformat; break; } } Add_Mapping(atom, format, dformat) Atom atom; char *format; char *dformat; { thunk t; if (!_property_formats) _property_formats = Create_Thunk_List(); t.value=atom; t.format=format; t.dformat=dformat; _property_formats = Add_Thunk(_property_formats, t); } /* * * Setup_Mapping: Routine to setup default atom to format, dformat mapping: * */ typedef struct { Atom atom; char *format; char *dformat; } _default_mapping; _default_mapping _default_mappings[] = { /* * General types: */ { XA_INTEGER, "0i", 0 }, { XA_CARDINAL, "0c", 0 }, { XA_STRING, "8s", 0 }, { XA_ATOM, "32a", 0 }, { XA_POINT, "16ii", " = $0, $1\n" }, { XA_RECTANGLE, "16iicc", ":\n\t\tupper left corner: $0, $1\n\ \t\tsize: $2 by $3\n" }, { XA_ARC, "16iiccii", ":\n\t\tarc at $0, $1\n\ \t\tsize: $2 by $3\n\ \t\tfrom angle $4 to angle $5\n" }, { XA_FONT, "32x", ": font id # $0\n" }, { XA_DRAWABLE, "32x", ": drawable id # $0\n" }, { XA_WINDOW, "32x", ": window id # $0\n" }, { XA_PIXMAP, "32x", ": pixmap id # $0\n" }, { XA_BITMAP, "32x", ": bitmap id # $0\n" }, { XA_VISUALID, "32x", ": visual id # $0\n" }, { XA_COLORMAP, "32x", ": colormap id # $0\n" }, { XA_CURSOR, "32x", ": cursor id # $0\n" }, { XA_RGB_COLOR_MAP, "32xccccccc", ":\n\ \t\tcolormap id #: $0\n\ \t\tred-max: $1\n\ \t\tred-mult: $2\n\ \t\tgreen-max: $3\n\ \t\tgreen-mult: $4\n\ \t\tblue-max: $5\n\ \t\tblue-mult: $6\n\ \t\tbase-pixel: $7\n" }, /* * Window manager types: */ { XA_WM_COMMAND, "8s", " = { $0+ }\n" }, { XA_WM_HINTS, "32mbcxxiixx", ":\n\ ?m0(\t\tClient accepts input or input focus: $1\n)\ ?m1(\t\tInitial state is \ ?$2=0(Don't Care State)\ ?$2=1(Normal State)\ ?$2=2(Zoomed State)\ ?$2=3(Iconic State)\ ?$2=4(Inactive State)\ .\n)\ ?m2(\t\tbitmap id # to use for icon: $3\n)\ ?m5(\t\tbitmap id # of mask for icon: $7\n)\ ?m3(\t\twindow id # to use for icon: $4\n)\ ?m4(\t\tstarting position for icon: $5, $6\n)\ ?m6(\t\twindow id # of group leader: $8\n)" }, { XA_WM_SIZE_HINTS, "32mii", ":\n\ ?m0(\t\tuser specified location: $1, $2\n)\ ?m2(\t\tprogram specified location: $1, $2\n)\ ?m1(\t\tuser specified size: $3 by $4\n)\ ?m3(\t\tprogram specified size: $3 by $4\n)\ ?m4(\t\tprogram specified minimum size: $5 by $6\n)\ ?m5(\t\tprogram specified maximum size: $7 by $8\n)\ ?m6(\t\tprogram specified resize increment: $9 by $10\n)\ ?m7(\t\tprogram specified minimum aspect ratio: $11/$12\n\ \t\tprogram specified maximum aspect ratio: $13/$14\n)\ ?m8(\t\tprogram specified base size: $15 by $16\n)\ ?m9(\t\twindow gravity: \ ?$17=0(Forget)\ ?$17=1(NorthWest)\ ?$17=2(North)\ ?$17=3(NorthEast)\ ?$17=4(West)\ ?$17=5(Center)\ ?$17=6(East)\ ?$17=7(SouthWest)\ ?$17=8(South)\ ?$17=9(SouthEast)\ ?$17=10(Static)\ \n)"}, { XA_WM_ICON_SIZE, "32cccccc", ":\n\ \t\tminimum icon size: $0 by $1\n\ \t\tmaximum icon size: $2 by $3\n\ \t\tincremental size change: $4 by $5\n" }, /* * Font specific mapping of property names to types: */ { XA_MIN_SPACE, "32c", 0 }, { XA_NORM_SPACE, "32c", 0 }, { XA_MAX_SPACE, "32c", 0 }, { XA_END_SPACE, "32c", 0 }, { XA_SUPERSCRIPT_X, "32i", 0 }, { XA_SUPERSCRIPT_Y, "32i", 0 }, { XA_SUBSCRIPT_X, "32i", 0 }, { XA_SUBSCRIPT_Y, "32i", 0 }, { XA_UNDERLINE_POSITION, "32i", 0 }, { XA_UNDERLINE_THICKNESS, "32c", 0 }, { XA_STRIKEOUT_ASCENT, "32i", 0 }, { XA_STRIKEOUT_DESCENT, "32i", 0 }, { XA_ITALIC_ANGLE, "32i", 0 }, { XA_X_HEIGHT, "32i", 0 }, { XA_QUAD_WIDTH, "32i", 0 }, { XA_WEIGHT, "32c", 0 }, { XA_POINT_SIZE, "32c", 0 }, { XA_RESOLUTION, "32c", 0 }, { XA_COPYRIGHT, "32a", 0 }, { XA_NOTICE, "32a", 0 }, { XA_FONT_NAME, "32a", 0 }, { XA_FAMILY_NAME, "32a", 0 }, { XA_FULL_NAME, "32a", 0 }, { 0, 0, 0 } }; Setup_Mapping() { _default_mapping *dmap = _default_mappings; Atom wm_state = XInternAtom (dpy, "WM_STATE", True); Atom wm_colormap_windows = XInternAtom (dpy, "WM_COLORMAP_WINDOWS", True); Atom wm_protocols = XInternAtom (dpy, "WM_PROTOCOLS", True); while (dmap->format) { Add_Mapping( dmap->atom, dmap->format, dmap->dformat ); dmap++; } /* * grok WM_STATE for debugging window and session managers */ if (wm_state != None) { Add_Mapping (wm_state, "32cx", ":\n\ \t\twindow state: ?$0=0(Withdrawn)?$0=1(Normal)?$0=3(Iconic)\n\ \t\ticon window: $1\n"); } if (wm_colormap_windows != None) { Add_Mapping (wm_colormap_windows, "32x", ": window id # $0+\n"); } if (wm_protocols != None) { Add_Mapping (wm_protocols, "32a", ": protocols $0+\n"); } } /* * Read_Mapping: routine to read in additional mappings from a stream * already open for reading. */ Read_Mappings(stream) FILE *stream; { char format_buffer[100]; char name[1000], *dformat, *format; int count, c; Atom atom, Parse_Atom(); while ((count=fscanf(stream," %990s %90s ",name,format_buffer))!=EOF) { if (count != 2) Fatal_Error("Bad format file format."); atom = Parse_Atom(name, False); format = Copy_String(format_buffer); Read_White_Space(stream); dformat = D_DFORMAT; c = getc(stream); ungetc(c, stream); if (c==(int)'\'') dformat = Read_Quoted(stream); Add_Mapping(atom, format, dformat); } } /* * * Formatting Routines: a group of routines to translate from various * values to a static read-only string useful for output. * * Routines: Format_Hex, Format_Unsigned, Format_Signed, Format_Atom, * Format_Mask_Word, Format_Bool, Format_String, Format_Len_String. * * All of the above routines take a long except for Format_String and * Format_Len_String. * */ static char _formatting_buffer[MAXSTR+100]; static char _formatting_buffer2[10]; char *Format_Hex(word) long word; { sprintf(_formatting_buffer2, "0x%lx", word); return(_formatting_buffer2); } char *Format_Unsigned(word) long word; { sprintf(_formatting_buffer2, "%lu", word); return(_formatting_buffer2); } char *Format_Signed(word) long word; { sprintf(_formatting_buffer2, "%ld", word); return(_formatting_buffer2); } char *Format_Atom(atom) Atom atom; { char *name; name=XGetAtomName(dpy, atom); if (!name) sprintf(_formatting_buffer, "undefined atom # 0x%lx", atom); else strncpy(_formatting_buffer, name, MAXSTR); return(_formatting_buffer); } char *Format_Mask_Word(word) long word; { long bit_mask, bit; int seen = 0; strcpy(_formatting_buffer, "{MASK: "); for (bit=0, bit_mask=1; bit<=sizeof(long)*8; bit++, bit_mask<<=1) { if (bit_mask & word) { if (seen) { strcat(_formatting_buffer, ", "); } seen=1; strcat(_formatting_buffer, Format_Unsigned(bit)); } } strcat(_formatting_buffer, "}"); return(_formatting_buffer); } char *Format_Bool(value) long value; { if (!value) return("False"); return("True"); } static char *_buf_ptr; static int _buf_len; _put_char(c) char c; { if (--_buf_len<0) { _buf_ptr[0]='\0'; return; } _buf_ptr++[0] = c; } _format_char(c) char c; { switch (c) { case '\\': case '\"': _put_char('\\'); _put_char(c); break; case '\n': _put_char('\\'); _put_char('n'); break; case '\t': _put_char('\\'); _put_char('t'); break; default: if (c<' ') { _put_char('\\'); sprintf(_buf_ptr, "%o", (int) c); _buf_ptr += strlen(_buf_ptr); _buf_len -= strlen(_buf_ptr); } else _put_char(c); } } char *Format_String(string) char *string; { char c; _buf_ptr = _formatting_buffer; _buf_len = MAXSTR; _put_char('\"'); while (c = string++[0]) _format_char(c); _buf_len += 3; _put_char('\"'); _put_char('\0'); return(_formatting_buffer); } char *Format_Len_String(string, len) char *string; int len; { char *data, *result; data = (char *) Malloc(len+1); #ifdef VMS memcpy(data, string, len); #else bcopy(string, data, len); #endif data[len]='\0'; result = Format_String(data); free(data); return(result); } /* * * Parsing Routines: a group of routines to parse strings into values * * Routines: Parse_Atom, Scan_Long, Skip_Past_Right_Paran, Scan_Octal * * Routines of the form Parse_XXX take a string which is parsed to a value. * Routines of the form Scan_XXX take a string, parse the beginning to a value, * and return the rest of the string. The value is returned via. the last * parameter. All numeric values are longs! * */ char *Skip_Digits(string) char *string; { while (isdigit(string[0])) string++; return(string); } char *Scan_Long(string, value) char *string; long *value; { if (!isdigit(*string)) Fatal_Error("Bad number: %s.", string); *value = atol(string); return(Skip_Digits(string)); } char *Scan_Octal(string, value) char *string; long *value; { if (sscanf(string, "%lo", value)!=1) Fatal_Error("Bad octal number: %s.", string); return(Skip_Digits(string)); } Atom Parse_Atom(name, only_if_exists) char *name; int only_if_exists; { Atom atom; if ((atom = XInternAtom(dpy, name, only_if_exists))==None) return(0); return(atom); } char *Skip_Past_Right_Paran(string) char *string; { char c; int nesting=0; while (c=string++[0], c!=')' || nesting) switch (c) { case '\0': Fatal_Error("Missing ')'."); case '(': nesting++; break; case ')': nesting--; break; case '\\': string++; break; } return(string); } /* * * The Format Manager: a group of routines to manage "formats" * */ int Is_A_Format(string) char *string; { return(isdigit(string[0])); } int Get_Format_Size(format) char *format; { long size; Scan_Long(format, &size); /* Check for legal sizes */ if (size != 0 && size != 8 && size != 16 && size != 32) Fatal_Error("bad format: %s", format); return((int) size); } char Get_Format_Char(format, i) char *format; int i; { long size; /* Remove # at front of format */ format = Scan_Long(format, &size); if (!*format) Fatal_Error("bad format: %s", format); /* Last character repeats forever... */ if (i>=strlen(format)) i=strlen(format)-1; return(format[i]); } char *Format_Thunk(t, format_char) thunk t; char format_char; { long value; value = t.value; switch (format_char) { case 's': return(Format_Len_String(t.extra_value, t.value)); case 'x': return(Format_Hex(value)); case 'c': return(Format_Unsigned(value)); case 'i': return(Format_Signed(value)); case 'b': return(Format_Bool(value)); case 'm': return(Format_Mask_Word(value)); case 'a': return(Format_Atom(value)); default: Fatal_Error("bad format character: %c", format_char); } } char *Format_Thunk_I(thunks, format, i) thunk *thunks; char *format; int i; { if (i >= thunks->thunk_count) return(""); return(Format_Thunk(thunks[i], Get_Format_Char(format, i))); } long Mask_Word(thunks, format) thunk *thunks; char *format; { int j; for (j=0; jthunk_count; i++) { if (seen) printf(", "); seen = 1; printf("%s", Format_Thunk_I(thunks, format, (int) i)); } } else printf("%s", Format_Thunk_I(thunks, format, (int) i)); return(dformat); } int Mask_Bit_I(thunks, format, i) thunk *thunks; char *format; int i; { long value; value = Mask_Word(thunks, format); value = value & (1L<=thunks->thunk_count) i=thunks->thunk_count; *value = thunks[i].value; } else if (*string=='m') { string = Scan_Long(++string, &i); *value = Mask_Bit_I(thunks, format, (int) i); } else Fatal_Error("Bad term: %s.", string); return(string); } char *Scan_Exp(string, thunks, format, value) thunk *thunks; char *string, *format; long *value; { long temp; if (string[0]=='(') { string = Scan_Exp(++string, thunks, format, value); if (string[0]!=')') Fatal_Error("Missing ')'"); return(++string); } if (string[0]=='!') { string = Scan_Exp(++string, thunks, format, value); *value = !*value; return(string); } string = Scan_Term(string, thunks, format, value); if (string[0]=='=') { string = Scan_Exp(++string, thunks, format, &temp); *value = *value == temp; } return(string); } char *Handle_Question_Mark(dformat, thunks, format) thunk *thunks; char *dformat, *format; { long true; dformat = Scan_Exp(dformat, thunks, format, &true); if (*dformat!='(') Fatal_Error("Bad conditional: '(' expected: %s.", dformat); ++dformat; if (!true) dformat = Skip_Past_Right_Paran(dformat); return(dformat); } Display_Property(thunks, dformat, format) thunk *thunks; char *dformat, *format; { char c; while (c = *(dformat++)) switch (c) { case ')': continue; case '\\': dformat = Handle_Backslash(dformat); continue; case '$': dformat = Handle_Dollar_sign(dformat, thunks, format); continue; case '?': dformat = Handle_Question_Mark(dformat, thunks, format); continue; default: putchar(c); continue; } } /* * * Routines to convert property data to and from thunks * */ long Extract_Value(pointer, length, size, signedp) char **pointer; int *length; int size; { long value, mask; switch (size) { case 8: value = (long) * (char *) *pointer; *pointer += 1; *length -= 1; mask = 0xff; break; case 16: value = (long) * (short *) *pointer; *pointer += 2; *length -= 2; mask = 0xffff; break; default: /* Error */ case 32: value = (long) * (long *) *pointer; *pointer += 4; *length -= 4; mask = 0xffffffff; break; } if (!signedp) value &= mask; return(value); } long Extract_Len_String(pointer, length, size, string) char **pointer; int *length; int size; char **string; { int len; if (size!=8) Fatal_Error("can't use format character 's' with any size except 8."); len=0; *string = *pointer; while ((len++, --*length, *((*pointer)++)) && *length>0); return(len); } thunk *Break_Down_Property(pointer, length, format, size) char *pointer, *format; int length, size; { thunk *thunks; thunk t; int i; char format_char; thunks = Create_Thunk_List(); i=0; while (length>=(size/8)) { format_char = Get_Format_Char(format, i); if (format_char=='s') t.value=Extract_Len_String(&pointer,&length,size,&t.extra_value); else t.value=Extract_Value(&pointer,&length,size,format_char=='i'); thunks = Add_Thunk(thunks, t); i++; } return(thunks); } /* * * Routines for parsing command line: * */ usage() { char **cpp; static char *help_message[] = { "where options include:", " -grammar print out full grammar for command line", " -display host:dpy the X server to contact", " -id id resource id of window to examine", " -name name name of window to examine", " -font name name of font to examine", " -remove propname name of property to remove", " -root examine the root window", " -len n display at most n bytes of any property", " -notype do not display the type field", " -fs filename where to look for formats for properties", " -frame don't ignore window manager frames", " -f propname format [dformat] formats to use for property of given name", " -spy examine window properties forever", NULL}; fflush (stdout); fprintf (stderr, "usage: %s [-options ...] [[format [dformat]] atom]\n\n", program_name); for (cpp = help_message; *cpp; cpp++) { fprintf (stderr, "%s\n", *cpp); } fprintf (stderr, "\n"); exit (1); } grammar () { printf ("Grammar for xprop:\n\n"); printf("\t%s [] [