chiark / gitweb /
bashrc: set +H
[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 #ifndef PATH_MAX
73 #define PATH_MAX 1024
74 #endif
75
76 #define forever for(;;)
77
78 /* #define NOXWINDOWS /* Define this to remove the X handling code */
79 /* #define NODETACH /* Define this to stop autotitle from forking ever */
80 /* #define NOXTERM /* Define this to make autotitle not set xterm's titles */
81 /* #define NOSTDOUT /* Define this to disable the stdout output */
82 #define DEFAULTTITLE "%h - %d. %Y.[U..[U..[D./..%X.L]..R]./.. %U%?G(%G)%!"
83 #define DEFAULTICON "%h: %d2. %Y.[U..[U..[D./..%X.L]..R]./.."
84
85 #define BUILDMAX 10000
86 #define SMALLBUFMAX 100
87 #define MAXSCALED 50
88
89 #ifndef __HPUX
90 static char lto_buf[20];
91 char *ltoa(n) long n; { sprintf(lto_buf,"%ld",n); return lto_buf; }
92 char *ltoaoct(n) long n; { sprintf(lto_buf,"%lo",n); return lto_buf; }
93 #else
94 # define ltoaoct(n) ltostr((n),8)
95 #endif
96
97 #ifndef NOXWINDOWS
98 struct geom {
99   int valid;
100   struct geom *child;
101   Window window, root;
102   int x,y;
103   unsigned w,h;
104 };
105 void geom_force();
106 struct geom wg, rg={0,&wg};
107 void X_force();
108 int X_error_handler();
109 #endif /* !NOXWINDOWS */
110
111 char *user_name(), *user_nameonly(), *user_home();
112 void user_force();
113 gid_t user_gid();
114 struct passwd *pwent;
115 void uid_force();
116 uid_t uid;
117
118 char *group_name();
119 void group_force();
120 struct group *grent;
121 void gid_force();
122 gid_t gid;
123
124 char *cwd_force();
125 void cwd_setmode();
126 int cwd_mode;
127
128 void mkstring();
129
130 char build[BUILDMAX], *bp;
131 char *bp_end= build+BUILDMAX;
132 char *cp;
133 char *envuser=0;
134
135 int umsk;
136
137 #ifndef NOXWINDOWS
138 Display *display;
139 Window window;
140 #endif
141 int iflevel, ifskiplevel;
142
143 void add_string(), add_number(), add_scaled(), add_currentdir();
144 void add_umask(), detach(), add_envir();
145 char *scaledstr_chop();
146
147 main(){
148   char *controlstring, *termtype;
149   int child, newfd;
150
151   umsk= umask(0);  umask(umsk);
152
153 #ifndef NOSTDOUT
154   mkstring(0,0, "AUTOTITLE_STDOUT","");
155   if (*build) {
156     write(1,build,strlen(build));
157   }
158 #endif /* !NOSTDOUT */
159
160 #ifndef NOXTERM
161   termtype= getenv("TERM");
162   if (termtype && !strcmp(termtype,"xterm")) {
163 # if !defined(NODETACH) && !defined(NOEARLYDETACH)
164     newfd=dup(2);
165     detach();
166     dup(newfd);
167     close(newfd);
168 # endif
169     mkstring("\033]2;","\007", "AUTOTITLE_WINDOW",DEFAULTTITLE);
170     write(2,build,strlen(build));
171     mkstring("\033]1;","\007", "AUTOTITLE_ICON",DEFAULTICON);
172     write(2,build,strlen(build));
173 # if !defined(NODETACH) && defined(NOEARLYDETACH)
174     detach();
175 # endif
176   } else {
177 #endif /* !NOXTERM */
178 #ifndef NOXWINDOWS
179 # ifndef NODETACH
180     detach();
181 # endif /* !NODETACH */
182     X_force();
183     if (window) {
184       mkstring(0,0, "AUTOTITLE_WINDOW",DEFAULTTITLE);
185       XStoreName(display,window,build);
186       mkstring(0,0, "AUTOTITLE_ICON",DEFAULTICON);
187       XSetIconName(display,window,build);
188       XSync(display,0);
189     }
190 #endif /* ~ NOXWINDOWS */
191 #ifndef NOXTERM
192   }
193 #endif
194   exit(0);
195 }
196
197 void mkstring(initialstring,finalstring,controlenv,controlstring)
198      char *initialstring, *finalstring;
199      char *controlenv, *controlstring;
200 {
201   static char *envuser=0, *envstring, *envgroup=0;
202   int inc, chkum, ifskipthis;
203   char smallbuffer[SMALLBUFMAX+1], *p;
204
205   envstring= getenv(controlenv);
206   if (envstring) controlstring= envstring;
207
208   bp= build;
209   ifskiplevel=0; iflevel=0;
210   cp=controlstring;
211   if (initialstring) add_string(initialstring);
212   forever {
213     char *pcp= strchr(cp,'%');
214     if (!pcp) {
215       add_string(cp);
216       break;
217     }
218     *pcp= 0;
219     add_string(cp);
220     cp= pcp+1;
221     switch (*cp++) {
222 #ifndef NOXWINDOWS
223     case 'y': geom_force(&wg); add_number(wg.y); break;
224     case 'Y': geom_force(&rg); add_scaled(wg.y,rg.h-wg.h+1); break;
225     case 'x': geom_force(&wg); add_number(wg.x); break;
226     case 'X': geom_force(&rg); add_scaled(wg.x,rg.w-wg.w+1); break;
227 #else
228     case 'X': case 'Y': add_scaled(INT_MAX,0); /* skip scaled parameter */
229     case 'x': case 'y': break;
230 #endif /* !NOXWINDOWS */
231     case 'd': add_currentdir(); break;
232     case 'G': add_string(group_name()); break; 
233     case 'g': gid_force(); add_number((long)gid); break;
234     case 'U': add_string(user_name()); break;
235     case 'u': uid_force(); add_number((long)uid); break;
236     case 'n': add_number(nice(0)); break;
237     case 'N': add_scaled(nice(0)+20,40); break;
238     case 'm': add_string(ltoaoct((long)umsk)); break;
239     case 'M': add_umask(umsk); break;
240     case 'E': add_envir(); break;
241     case 'h': case 'H':
242       if (!gethostname(smallbuffer,SMALLBUFMAX)) {
243         if (cp[-1] == 'h') {
244           p=strchr(smallbuffer,'.');
245           if (p) *p=0;
246         }
247         add_string(smallbuffer);
248       }
249       break;
250     case '!':
251       if (iflevel>0) iflevel--;
252       if (ifskiplevel>0) ifskiplevel--;
253       break;
254     case '%': add_string("%%"); break;
255     case '?':
256       switch (*cp++) {
257       case 'E':
258         p= strchr(cp,'.');
259         if (p) {
260           *p=0;
261           ifskipthis= 0!=getenv(cp);
262           cp= p+1;
263         } else ifskipthis=0;
264         break;
265       case 'g': gid_force(); ifskipthis= (gid==user_gid()); break;
266       case 'G':
267         gid_force(); 
268         envgroup= envgroup?envgroup: getenv("GROUP");
269         ifskipthis=
270           envgroup ? (gid==atol(envgroup) || !strcmp(group_name(),envgroup))
271                    : (gid==user_gid());
272         break;
273       case 'u':
274         envuser= envuser?envuser: getenv("USER");
275         envuser= envuser?envuser: getenv("LOGNAME");
276         ifskipthis= (envuser && user_nameonly() &&
277                      !strcmp(envuser,user_nameonly()));
278         break;
279       case 'n': ifskipthis= (nice(0)==0); break;
280       case 'm':
281         if (sscanf(cp,"%3o%n",&chkum,&inc)==1) {
282           cp+=inc;
283           ifskipthis= umsk==chkum;
284         }
285       } /* switch() for if's */
286       iflevel+=1;
287       ifskiplevel+= ifskiplevel ? 1 : ifskipthis;
288       break;
289     } /* switch for %'s */
290   }
291   if (finalstring) add_string(finalstring);
292 }
293
294 void add_envir() {
295   char *p, *e;
296
297   p= strchr(cp,'.');
298   if (p) {
299     *p=0;
300     e=getenv(cp);
301     cp=p+1;
302     if (e) add_string(e);
303   }
304 }
305     
306 void add_string(ns)
307      char *ns;
308 {
309   char *nbp= bp+strlen(ns);
310   if (ifskiplevel || nbp > bp_end) return;
311   strcpy(bp,ns);
312   bp=nbp;
313 }
314
315 void add_number(number)
316      long number;
317 {
318   add_string(ltoa(number));
319 }
320
321 void add_scaled(num,denom)
322      int num,denom;
323 {
324   char *sd, *sn[MAXSCALED];
325   int n= 0;
326   double v;
327
328   while (*cp && n<MAXSCALED && *cp!='/') {
329     sn[n++]= scaledstr_chop();
330   }
331   if (*cp) cp++; /* skip the '/' */
332   sd= scaledstr_chop();
333   if (num==INT_MAX && denom==0) return;
334   v= denom? (num/(double)denom) : -1;
335   if (v<0 || v>1) {
336     add_string(sd);
337   } else {
338     add_string(sn[(int)(v*n)]);
339   }
340 }
341
342 char *scaledstr_chop() {
343   char tc= *cp++;
344   char *rp= cp;
345   char *ep= strchr(cp,tc);
346   if (ep) {
347     cp= ep;
348     *cp++= 0;
349   }
350   return(rp);
351 }
352
353 void add_umask(u)
354      int u;
355 {
356   char buf[4], *bp;
357   int oi, h;
358  
359   if (ifskiplevel) return;
360
361   for (oi=3;
362        oi;
363        oi--, u<<=3) {
364     strcpy(buf,"rwx");
365     h= ((u>>6) & 7);
366     for (bp=buf;
367          bp<buf+3;
368          h<<=1, bp++) {
369       if (h&4) *bp='-';
370     }
371     add_string(buf);
372   }
373 }
374
375 void add_currentdir() {
376   char *homestring="", *p, *homedir, *amdhome;
377   int comps=10000, chars, totalslashes, skipslashes, len, homeskip;
378   char *buf, nbuf[PATH_MAX+10], nhome[PATH_MAX+10];
379
380   amdhome= getenv("HOME_AMDTMP");
381   homedir= getenv("HOME");
382   cwd_mode= 0;
383   if (ifskiplevel) return;
384   while (*cp && *cp!='.') {
385     if (*cp == 'U') {
386       homedir= 0;
387       cp++;
388     } else if (*cp == 'E') {
389       cwd_setmode(2); cp++;
390     } else if (*cp == 'e') {
391       cwd_setmode(1); cp++;
392     } else if (*cp == '~') {
393       homestring="~/";
394       cp++;
395     } else if (isdigit(*cp)) {
396       if (sscanf(cp,"%d%n",&comps,&chars)==1) {
397         cp+= chars;
398       } else
399         cp++;
400     }
401   }
402   if (*cp) cp++;
403   if (!homedir) {
404     homedir= user_home();
405   }
406   buf= cwd_force();
407   homeskip= 0;
408   if (homedir) {
409     len=strlen(homedir);
410     if (homedir[len-1] != '/') {
411       strcpy(nhome, homedir);
412       strcat(nhome, "/");
413       homedir= nhome;
414       len++;
415     }
416     if (len>1 && !strncmp(buf,homedir,len-1) &&
417         (buf[len-1]=='/' || !(buf[len-1]))) {
418       strcpy(nbuf,homestring);
419       strcat(nbuf,buf+len);
420       buf= nbuf;
421       if (!strlen(buf)) buf="~";
422       homeskip= strlen(homestring);
423     }
424   }
425   if (amdhome) {
426     len=strlen(amdhome);
427     if (amdhome[len-1] != '/') {
428       strcpy(nhome, amdhome);
429       strcat(nhome, "/");
430       amdhome= nhome;
431       len++;
432     }
433     if (len>1 && !strncmp(buf,amdhome,len-1) &&
434         (buf[len-1]=='/' || !(buf[len-1]))) {
435       strcpy(nbuf,homestring);
436       strcat(nbuf,buf+len);
437       buf= nbuf;
438       if (!strlen(buf)) buf="~";
439       homeskip= strlen(homestring);
440     }
441   }
442   totalslashes=0;
443   for (p= buf+homeskip; *p; p++)
444     if (*p=='/') totalslashes++;
445   while (p>buf && p[-1]=='/') { *--p=0; totalslashes--; }
446   skipslashes= totalslashes-comps+1;
447
448   for (p= buf+homeskip;
449        skipslashes>0;
450        p++)
451     if (*p=='/') skipslashes--;
452   if (p==buf+1 || p==buf+homeskip || p==buf+homeskip) p=buf;
453
454   add_string(p);
455 }
456
457 gid_t user_gid() {
458   user_force();
459   return pwent? pwent->pw_gid: -1;
460 }
461
462 char *user_nameonly() {
463   user_force();
464   return pwent? pwent->pw_name: 0;
465 }
466
467 char *user_home() {
468   user_force();
469   return pwent? pwent->pw_dir: 0;
470 }
471
472 char *user_name() {
473   user_force();
474   if (pwent) return(pwent->pw_name);
475   return(ltoa((long)uid));
476 }
477
478 void uid_force() {
479   static int valid;
480   if (valid) return;
481   uid= getuid();
482 }
483
484 void user_force() {
485   static int valid;
486   if (valid) return;
487   uid_force();
488   valid=1;
489   pwent= getpwuid(uid);
490 }
491
492 char *group_name() {
493   group_force();
494   if (grent) return(grent->gr_name);
495   return(ltoa((long)gid));
496 }
497
498 void group_force() {
499   static int valid;
500   if (valid) return;
501   gid_force();
502   valid=1;
503   grent= getgrgid(gid);
504 }
505
506 void gid_force() {
507   static int valid;
508   if (valid) return;
509   gid= getgid();
510 }
511
512 void cwd_setmode(mode)
513      int mode;
514 {
515   cwd_mode=mode; /* 0= default,  1= 'e',  2= 'E' */
516 }
517
518 int different_files(a,b)
519      char *a, *b;
520 {
521   struct stat staa, stab;
522
523   return stat(a,&staa) || stat(b,&stab) ||
524     staa.st_ino != stab.st_ino || staa.st_dev != stab.st_dev;
525 }
526         
527 char *cwd_force() {
528   static int valid=0, fail;
529   static char buffer[PATH_MAX+1];
530   if (!valid) {
531     if (cwd_mode!=1 /* not 'e' */) {
532       char *result= getenv("PWD");
533       if (result && (cwd_mode /* 'E' */ || !different_files(".",result))) {
534         strncpy(buffer,result,PATH_MAX); buffer[PATH_MAX]=0;
535         valid= 1;
536       }
537     }
538     if (!valid) {
539       valid= getcwd(buffer,PATH_MAX)!=0;
540     }
541     valid=1;
542   }
543   return buffer;
544 }
545
546 #if (!defined(NOXWINDOWS) || !defined(NOXTERM)) && !defined(NODETACH)
547 void detach() {
548   close(0); close(1); close(2); if (fork()!=0) exit(0);
549 }
550 #endif /* !NODETACH */
551
552 #ifndef NOXWINDOWS
553
554 void X_force() {
555   static valid;
556   char *widname, *dispname;
557
558   if (valid) return;
559   window= (XID)0; display= (Display*)0;
560   XSetErrorHandler(X_error_handler);
561
562   dispname= getenv("DISPLAY");
563   if (!dispname) return;
564   display= XOpenDisplay(dispname);
565   if (!display) return;
566
567   widname= getenv("WINDOWID");
568   if (!widname) return;
569   window= (XID)(atol(widname));
570   wg.window= window;
571 }
572
573 int X_error_handler(edisp,eevent)
574      Display *edisp;
575      XErrorEvent *eevent;
576 {
577   display= (Display*)0;
578   window= (XID)0;
579   wg.window= rg.window= window;
580   wg.x= wg.y= rg.x= rg.y= -1000;
581   return(0);
582 }
583
584 void geom_force(g)
585      struct geom *g;
586 {
587   unsigned dummyu;
588   int dummys;
589   Window dummyw;
590
591   if (g->valid) return;
592   if (g->child) {
593     geom_force(g->child);
594     g->window= g->child->root;
595   }
596   X_force();
597   if (g->window) {
598     XGetGeometry(display,
599                  g->window,&g->root,
600                  &dummys,&dummys,&g->w,&g->h,
601                  &dummyu,&dummyu);
602     XTranslateCoordinates(display,g->window,g->root,0,0,&g->x,&g->y,&dummyw);
603   } else {
604     g->x= -1000;
605     g->y= -1000;
606   }
607   g->valid= 1;
608 }
609 #endif /* !NOXWINDOWS */