chiark / gitweb /
Found on jura.
[ian-dotfiles.git] / autotitle / autotitle.c
1 /*
2  * Autotitle - copyright (C)1991 Ian Jackson (iwj10@phx.cam.ac.uk)
3  *
4  * Automatically set window titles, prompts etc.
5  * 
6  * Will output the string in AUTOTITLE_STDOUT (in the environment)
7  * to standard output, eg for use in your prompt.
8  * If it can find your X server or your TERM is xterm it will
9  * set your icon name to AUTOTITLE_ICON and your window name
10  * to AUTOTITLE_WINDOW.
11  *
12  * Defaults are, respectively,
13  *  "" (ie no output)
14  *  "%h: %d2. %Y.[U..[U..[D./..%X.L]..R]./.."
15  *  "%h - %d. %Y.[U..[U..[D./..%X.L]..R]./.. %U%?G(%G)%!"
16  *
17  * Environment configuration strings work a bit like printf etc:
18  *
19  * Conversion characters
20  *  %x                  Left edge position of window (if WINDOWID and DISPLAY set).
21  *  %y                  Top edge height.
22  *  %X.p..q..r./.s.     (pqrstrings)[ len(str)*ypos/(yscreen-ysize+1)+0.5 ]
23  *  %Y.p..q..r./.s.     Same for x,y.   If window is off screen, is string s.
24  *                      The dots are delimiters - any char allowed, must
25  *                      be same at both ends of one string p, q or r etc.
26  *                      Number of strings may be 1..oo but /.string. is mandatory.
27  *  %d***.              Current directory; examples of ***'s:
28  *      %d2.            Last two components only
29  *      %d~.            Display home directory as ~ (as opposed to '' or ~)
30  *      %de.            Never use PWD environment variable even if it is correct.
31  *      %dE.            Always use PWD environment variable.
32  *      %dU.            Use home directory for uid from passwd file, not $HOME
33  *      %d3~U.          Combination of the above.
34  *  %n                  Nice value
35  *  %N.p..q..r./.s.     Like %Y and %X only for nice value (range 0..39)
36  *  %U                  Name of uid
37  *  %u                  Numerical uid
38  *  %G                  Name of gid
39  *  %g                  Numerical gid
40  *  %E***.              Environment variable ***, or null string.
41  *  %m                  Umask in numerical form (3 digits) (as set by umask)
42  *  %M                  Umask in rwx--- form (shows perms of file opened 777)
43  *  %h                  Hostname, but truncated to just the host, not FQDN
44  *  %H                  Hostname - FQDN if available, otherwise just machine
45  *  %?g***%!            Do *** if gid != default for this uid
46  *  %?G***%!            Do *** if gid or group name != $GROUP (or uid's grp if no $GROUP)
47  *  %?u***%!            Do *** if uid != $USER (or $LOGNAME if no $USER)
48  *  %?n***%!            Do *** if nice value != 20
49  *  %?mXXX***%!         Do *** if umask != XXX
50  *  %?E###.***%!        Do *** if ### is not an environment variable.
51  *
52  */
53
54 #ifndef NOXWINDOWS
55 #include <X11/X.h>
56 #include <X11/Xlib.h>
57 #include <X11/Xutil.h>
58 #endif
59 #ifndef NOSTDLIB
60 #include <stdlib.h>
61 #endif
62 #include <stdio.h>
63 #include <sys/types.h>
64 #include <sys/stat.h>
65 #include <pwd.h>
66 #include <grp.h>
67 #include <limits.h>
68 #include <string.h>
69 #include <unistd.h>
70 #include <math.h>
71 #include <ctype.h>
72 #include <values.h>
73 #ifndef PATH_MAX
74 #define PATH_MAX 1024
75 #endif
76
77 #define forever for(;;)
78
79 /* #define NOXWINDOWS /* Define this to remove the X handling code */
80 /* #define NODETACH /* Define this to stop autotitle from forking ever */
81 /* #define NOXTERM /* Define this to make autotitle not set xterm's titles */
82 /* #define NOSTDOUT /* Define this to disable the stdout output */
83 #define DEFAULTTITLE "%h - %d. %Y.[U..[U..[D./..%X.L]..R]./.. %U%?G(%G)%!"
84 #define DEFAULTICON "%h: %d2. %Y.[U..[U..[D./..%X.L]..R]./.."
85
86 #define BUILDMAX 1000
87 #define SMALLBUFMAX 20
88 #define MAXSCALED 15
89
90 #ifndef __HPUX
91 static char lto_buf[20];
92 char *ltoa(n) long n; { sprintf(lto_buf,"%ld",n); return lto_buf; }
93 char *ltoaoct(n) long n; { sprintf(lto_buf,"%lo",n); return lto_buf; }
94 #else
95 # define ltoaoct(n) ltostr((n),8)
96 #endif
97
98 #ifndef NOXWINDOWS
99 struct geom {
100   int valid;
101   struct geom *child;
102   Window window, root;
103   int x,y;
104   unsigned w,h;
105 };
106 void geom_force();
107 struct geom wg, rg={0,&wg};
108 void X_force();
109 int X_error_handler();
110 #endif /* !NOXWINDOWS */
111
112 char *user_name(), *user_nameonly(), *user_home();
113 void user_force();
114 gid_t user_gid();
115 struct passwd *pwent;
116 void uid_force();
117 uid_t uid;
118
119 char *group_name();
120 void group_force();
121 struct group *grent;
122 void gid_force();
123 gid_t gid;
124
125 char *cwd_force();
126 void cwd_setmode();
127 int cwd_mode;
128
129 void mkstring();
130
131 char build[BUILDMAX], *bp;
132 char *bp_end= build+BUILDMAX;
133 char *cp;
134 char *envuser=0;
135
136 int umsk;
137
138 #ifndef NOXWINDOWS
139 Display *display;
140 Window window;
141 #endif
142 int iflevel, ifskiplevel;
143
144 void add_string(), add_number(), add_scaled(), add_currentdir();
145 void add_umask(), detach(), add_envir();
146 char *scaledstr_chop();
147
148 main(){
149   char *controlstring, *termtype;
150   int child, newfd;
151
152   umsk= umask(0);  umask(umsk);
153
154 #ifndef NOSTDOUT
155   mkstring(0,0, "AUTOTITLE_STDOUT","");
156   if (*build) {
157     write(1,build,strlen(build));
158   }
159 #endif /* !NOSTDOUT */
160
161 #ifndef NOXTERM
162   termtype= getenv("TERM");
163   if (termtype && !strcmp(termtype,"xterm")) {
164 # if !defined(NODETACH) && !defined(NOEARLYDETACH)
165     newfd=dup(2);
166     detach();
167     dup(newfd);
168     close(newfd);
169 # endif
170     mkstring("\033]2;","\007", "AUTOTITLE_WINDOW",DEFAULTTITLE);
171     write(2,build,strlen(build));
172     mkstring("\033]1;","\007", "AUTOTITLE_ICON",DEFAULTICON);
173     write(2,build,strlen(build));
174 # if !defined(NODETACH) && defined(NOEARLYDETACH)
175     detach();
176 # endif
177   } else {
178 #endif /* !NOXTERM */
179 #ifndef NOXWINDOWS
180 # ifndef NODETACH
181     detach();
182 # endif /* !NODETACH */
183     X_force();
184     if (window) {
185       mkstring(0,0, "AUTOTITLE_WINDOW",DEFAULTTITLE);
186       XStoreName(display,window,build);
187       mkstring(0,0, "AUTOTITLE_ICON",DEFAULTICON);
188       XSetIconName(display,window,build);
189       XSync(display,0);
190     }
191 #endif /* ~ NOXWINDOWS */
192 #ifndef NOXTERM
193   }
194 #endif
195   exit(0);
196 }
197
198 void mkstring(initialstring,finalstring,controlenv,controlstring)
199      char *initialstring, *finalstring;
200      char *controlenv, *controlstring;
201 {
202   static char *envuser=0, *envstring, *envgroup=0;
203   int inc, chkum, ifskipthis;
204   char smallbuffer[SMALLBUFMAX+1], *p;
205
206   envstring= getenv(controlenv);
207   if (envstring) controlstring= envstring;
208
209   bp= build;
210   ifskiplevel=0; iflevel=0;
211   cp=controlstring;
212   if (initialstring) add_string(initialstring);
213   forever {
214     char *pcp= strchr(cp,'%');
215     if (!pcp) {
216       add_string(cp);
217       break;
218     }
219     *pcp= 0;
220     add_string(cp);
221     cp= pcp+1;
222     switch (*cp++) {
223 #ifndef NOXWINDOWS
224     case 'y': geom_force(&wg); add_number(wg.y); break;
225     case 'Y': geom_force(&rg); add_scaled(wg.y,rg.h-wg.h+1); break;
226     case 'x': geom_force(&wg); add_number(wg.x); break;
227     case 'X': geom_force(&rg); add_scaled(wg.x,rg.w-wg.w+1); break;
228 #else
229     case 'X': case 'Y': add_scaled(MAXINT,0); /* skip scaled parameter */
230     case 'x': case 'y': break;
231 #endif /* !NOXWINDOWS */
232     case 'd': add_currentdir(); break;
233     case 'G': add_string(group_name()); break; 
234     case 'g': gid_force(); add_number((long)gid); break;
235     case 'U': add_string(user_name()); break;
236     case 'u': uid_force(); add_number((long)uid); break;
237     case 'n': add_number(nice(0)); break;
238     case 'N': add_scaled(nice(0)+20,40); break;
239     case 'm': add_string(ltoaoct((long)umsk)); break;
240     case 'M': add_umask(umsk); break;
241     case 'E': add_envir(); break;
242     case 'h': case 'H':
243       if (!gethostname(smallbuffer,SMALLBUFMAX)) {
244         if (cp[-1] == 'h') {
245           p=strchr(smallbuffer,'.');
246           if (p) *p=0;
247         }
248         add_string(smallbuffer);
249       }
250       break;
251     case '!':
252       if (iflevel>0) iflevel--;
253       if (ifskiplevel>0) ifskiplevel--;
254       break;
255     case '%': add_string("%%"); break;
256     case '?':
257       switch (*cp++) {
258       case 'E':
259         p= strchr(cp,'.');
260         if (p) {
261           *p=0;
262           ifskipthis= 0!=getenv(cp);
263           cp= p+1;
264         } else ifskipthis=0;
265         break;
266       case 'g': gid_force(); ifskipthis= (gid==user_gid()); break;
267       case 'G':
268         gid_force(); 
269         envgroup= envgroup?envgroup: getenv("GROUP");
270         ifskipthis=
271           envgroup ? (gid==atol(envgroup) || !strcmp(group_name(),envgroup))
272                    : (gid==user_gid());
273         break;
274       case 'u':
275         envuser= envuser?envuser: getenv("USER");
276         envuser= envuser?envuser: getenv("LOGNAME");
277         ifskipthis= (envuser && user_nameonly() &&
278                      !strcmp(envuser,user_nameonly()));
279         break;
280       case 'n': ifskipthis= (nice(0)==0); break;
281       case 'm':
282         if (sscanf(cp,"%3o%n",&chkum,&inc)==1) {
283           cp+=inc;
284           ifskipthis= umsk==chkum;
285         }
286       } /* switch() for if's */
287       iflevel+=1;
288       ifskiplevel+= ifskiplevel ? 1 : ifskipthis;
289       break;
290     } /* switch for %'s */
291   }
292   if (finalstring) add_string(finalstring);
293 }
294
295 void add_envir() {
296   char *p, *e;
297
298   p= strchr(cp,'.');
299   if (p) {
300     *p=0;
301     e=getenv(cp);
302     cp=p+1;
303     if (e) add_string(e);
304   }
305 }
306     
307 void add_string(ns)
308      char *ns;
309 {
310   char *nbp= bp+strlen(ns);
311   if (ifskiplevel || nbp > bp_end) return;
312   strcpy(bp,ns);
313   bp=nbp;
314 }
315
316 void add_number(number)
317      long number;
318 {
319   add_string(ltoa(number));
320 }
321
322 void add_scaled(num,denom)
323      int num,denom;
324 {
325   char *sd, *sn[MAXSCALED];
326   int n= 0;
327   double v;
328
329   while (*cp && n<MAXSCALED && *cp!='/') {
330     sn[n++]= scaledstr_chop();
331   }
332   if (*cp) cp++; /* skip the '/' */
333   sd= scaledstr_chop();
334   if (num==MAXINT && denom==0) return;
335   v= denom? (num/(double)denom) : -1;
336   if (v<0 || v>1) {
337     add_string(sd);
338   } else {
339     add_string(sn[(int)(v*n)]);
340   }
341 }
342
343 char *scaledstr_chop() {
344   char tc= *cp++;
345   char *rp= cp;
346   char *ep= strchr(cp,tc);
347   if (ep) {
348     cp= ep;
349     *cp++= 0;
350   }
351   return(rp);
352 }
353
354 void add_umask(u)
355      int u;
356 {
357   char buf[4], *bp;
358   int oi, h;
359  
360   if (ifskiplevel) return;
361
362   for (oi=3;
363        oi;
364        oi--, u<<=3) {
365     strcpy(buf,"rwx");
366     h= ((u>>6) & 7);
367     for (bp=buf;
368          bp<buf+3;
369          h<<=1, bp++) {
370       if (h&4) *bp='-';
371     }
372     add_string(buf);
373   }
374 }
375
376 void add_currentdir() {
377   char *homestring="", *p, *homedir, *amdhome;
378   int comps=10000, chars, totalslashes, skipslashes, len, homeskip;
379   char *buf, nbuf[PATH_MAX+10], nhome[PATH_MAX+10];
380
381   amdhome= getenv("HOME_AMDTMP");
382   homedir= getenv("HOME");
383   cwd_mode= 0;
384   if (ifskiplevel) return;
385   while (*cp && *cp!='.') {
386     if (*cp == 'U') {
387       homedir= 0;
388       cp++;
389     } else if (*cp == 'E') {
390       cwd_setmode(2); cp++;
391     } else if (*cp == 'e') {
392       cwd_setmode(1); cp++;
393     } else if (*cp == '~') {
394       homestring="~/";
395       cp++;
396     } else if (isdigit(*cp)) {
397       if (sscanf(cp,"%d%n",&comps,&chars)==1) {
398         cp+= chars;
399       } else
400         cp++;
401     }
402   }
403   if (*cp) cp++;
404   if (!homedir) {
405     homedir= user_home();
406   }
407   buf= cwd_force();
408   homeskip= 0;
409   if (homedir) {
410     len=strlen(homedir);
411     if (homedir[len-1] != '/') {
412       strcpy(nhome, homedir);
413       strcat(nhome, "/");
414       homedir= nhome;
415       len++;
416     }
417     if (len>1 && !strncmp(buf,homedir,len-1) &&
418         (buf[len-1]=='/' || !(buf[len-1]))) {
419       strcpy(nbuf,homestring);
420       strcat(nbuf,buf+len);
421       buf= nbuf;
422       if (!strlen(buf)) buf="~";
423       homeskip= strlen(homestring);
424     }
425   }
426   if (amdhome) {
427     len=strlen(amdhome);
428     if (amdhome[len-1] != '/') {
429       strcpy(nhome, amdhome);
430       strcat(nhome, "/");
431       amdhome= nhome;
432       len++;
433     }
434     if (len>1 && !strncmp(buf,amdhome,len-1) &&
435         (buf[len-1]=='/' || !(buf[len-1]))) {
436       strcpy(nbuf,homestring);
437       strcat(nbuf,buf+len);
438       buf= nbuf;
439       if (!strlen(buf)) buf="~";
440       homeskip= strlen(homestring);
441     }
442   }
443   totalslashes=0;
444   for (p= buf+homeskip; *p; p++)
445     if (*p=='/') totalslashes++;
446   while (p>buf && p[-1]=='/') { *--p=0; totalslashes--; }
447   skipslashes= totalslashes-comps+1;
448
449   for (p= buf+homeskip;
450        skipslashes>0;
451        p++)
452     if (*p=='/') skipslashes--;
453   if (p==buf+1 || p==buf+homeskip || p==buf+homeskip) p=buf;
454
455   add_string(p);
456 }
457
458 gid_t user_gid() {
459   user_force();
460   return pwent? pwent->pw_gid: -1;
461 }
462
463 char *user_nameonly() {
464   user_force();
465   return pwent? pwent->pw_name: 0;
466 }
467
468 char *user_home() {
469   user_force();
470   return pwent? pwent->pw_dir: 0;
471 }
472
473 char *user_name() {
474   user_force();
475   if (pwent) return(pwent->pw_name);
476   return(ltoa((long)uid));
477 }
478
479 void uid_force() {
480   static int valid;
481   if (valid) return;
482   uid= getuid();
483 }
484
485 void user_force() {
486   static int valid;
487   if (valid) return;
488   uid_force();
489   valid=1;
490   pwent= getpwuid(uid);
491 }
492
493 char *group_name() {
494   group_force();
495   if (grent) return(grent->gr_name);
496   return(ltoa((long)gid));
497 }
498
499 void group_force() {
500   static int valid;
501   if (valid) return;
502   gid_force();
503   valid=1;
504   grent= getgrgid(gid);
505 }
506
507 void gid_force() {
508   static int valid;
509   if (valid) return;
510   gid= getgid();
511 }
512
513 void cwd_setmode(mode)
514      int mode;
515 {
516   cwd_mode=mode; /* 0= default,  1= 'e',  2= 'E' */
517 }
518
519 int different_files(a,b)
520      char *a, *b;
521 {
522   struct stat staa, stab;
523
524   return stat(a,&staa) || stat(b,&stab) ||
525     staa.st_ino != stab.st_ino || staa.st_dev != stab.st_dev;
526 }
527         
528 char *cwd_force() {
529   static int valid=0, fail;
530   static char buffer[PATH_MAX+1];
531   if (!valid) {
532     if (cwd_mode!=1 /* not 'e' */) {
533       char *result= getenv("PWD");
534       if (result && (cwd_mode /* 'E' */ || !different_files(".",result))) {
535         strncpy(buffer,result,PATH_MAX); buffer[PATH_MAX]=0;
536         valid= 1;
537       }
538     }
539     if (!valid) {
540       valid= getcwd(buffer,PATH_MAX)!=0;
541     }
542     valid=1;
543   }
544   return buffer;
545 }
546
547 #if (!defined(NOXWINDOWS) || !defined(NOXTERM)) && !defined(NODETACH)
548 void detach() {
549   close(0); close(1); close(2); if (fork()!=0) exit(0);
550 }
551 #endif /* !NODETACH */
552
553 #ifndef NOXWINDOWS
554
555 void X_force() {
556   static valid;
557   char *widname, *dispname;
558
559   if (valid) return;
560   window= (XID)0; display= (Display*)0;
561   XSetErrorHandler(X_error_handler);
562
563   dispname= getenv("DISPLAY");
564   if (!dispname) return;
565   display= XOpenDisplay(dispname);
566   if (!display) return;
567
568   widname= getenv("WINDOWID");
569   if (!widname) return;
570   window= (XID)(atol(widname));
571   wg.window= window;
572 }
573
574 int X_error_handler(edisp,eevent)
575      Display *edisp;
576      XErrorEvent *eevent;
577 {
578   display= (Display*)0;
579   window= (XID)0;
580   wg.window= rg.window= window;
581   wg.x= wg.y= rg.x= rg.y= -1000;
582   return(0);
583 }
584
585 void geom_force(g)
586      struct geom *g;
587 {
588   unsigned dummyu;
589   int dummys;
590   Window dummyw;
591
592   if (g->valid) return;
593   if (g->child) {
594     geom_force(g->child);
595     g->window= g->child->root;
596   }
597   X_force();
598   if (g->window) {
599     XGetGeometry(display,
600                  g->window,&g->root,
601                  &dummys,&dummys,&g->w,&g->h,
602                  &dummyu,&dummyu);
603     XTranslateCoordinates(display,g->window,g->root,0,0,&g->x,&g->y,&dummyw);
604   } else {
605     g->x= -1000;
606     g->y= -1000;
607   }
608   g->valid= 1;
609 }
610 #endif /* !NOXWINDOWS */