home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #3 / NN_1993_3.iso / spool / alt / hackers / 1985 < prev    next >
Encoding:
Text File  |  1993-01-25  |  39.2 KB  |  1,429 lines

  1. Newsgroups: alt.hackers
  2. Path: sparky!uunet!zaphod.mps.ohio-state.edu!sol.ctr.columbia.edu!news.cs.columbia.edu!news!bm
  3. From: bm@shadow.columbia.edu (Blair MacIntyre)
  4. Subject: Re: .plan pipe to backfinger.
  5. In-Reply-To: gezelter@neon.cchem.berkeley.edu's message of 25 Jan 1993 22:12:36 GMT
  6. Message-ID: <C1Fy82.Lqx@cs.columbia.edu>
  7. Sender: news@cs.columbia.edu (The Daily News)
  8. Reply-To: Blair MacIntyre <bm@cs.columbia.edu>
  9. Organization: Columbia University
  10. References: <1k1okk$beu@agate.berkeley.edu>
  11. Date: Tue, 26 Jan 1993 03:12:01 GMT
  12. Approved: ack.
  13. Lines: 1414
  14.  
  15. >>>>> On 25 Jan 1993 22:12:36 GMT, gezelter@neon.cchem.berkeley.edu (Dan
  16. >>>>> Gezelter) said:
  17.  
  18. dan> Here's the latest version of the .plan pipe program that I've
  19. dan> inherited from numerous other people.  
  20.  
  21. I have a similar .plan pipe program (based on a simple version by Rich
  22. Salz which was modified and given to me by my old officemate Hamish
  23. MacDonald, which I then hacked to death) which uses TCL to parse a
  24. .planinit file in the users home dir and execute a certain function
  25. within it to create the output for the .plan
  26.  
  27. There is no documentation, but it seems fairly clean.  Like Dan, I
  28. haven't used this in a while in the FIFO version, since we are NFS'd
  29. like crazy here and pipes don't dig NFS access.  However, a compile time
  30. switch allows it to become a program which rewrites your .plan every N
  31. seconds (which is what I currently use.)  
  32.  
  33. If anyone wants to play with it, I make no guarantees.   There are two
  34. sample .planinit files in here, which you should be able to get the
  35. right idea from.  And, of course, you need to grab a copy of TCL from
  36. somewhere.  Nothing that's a problem for a hacker ...
  37.  
  38. It currently compiles to the non-FIFO version, so the trivial change to
  39. get it to compile the FIFO version is left as an exercise to the reader.
  40.  
  41. Have fun.   If you do anything cool with it, let me know!  (like
  42. figuring out how to do a complete ID of people fingering you)
  43.  
  44. ----snippity snip----
  45. : This is a shar archive.  Extract with sh, not csh.
  46. : The rest of this file will extract: 
  47. :
  48. :    .planinit
  49. :    .planinit2
  50. :    Makefile
  51. :    plan_proto.h
  52. :    plantcl.c
  53. :    tcl_cmds.c
  54. :    tcl_cmds.h
  55. :    uniqueproc.c
  56. :
  57. echo x - .planinit
  58. sed 's/^X//' > .planinit << '//go.sysin dd *'
  59. Xproc time12 hour24 {
  60. X    if {($hour24 % 12) == 0} \
  61. X        {return [format 12]} \
  62. X    else \
  63. X        {return [expr {$hour24 % 12}]}
  64. X}
  65. X
  66. Xproc timeampm hour24 {
  67. X    if {($hour24 < 12) || ($hour24 == 24)} \
  68. X        {return am} \
  69. X    else \
  70. X        {return pm}
  71. X}
  72. X
  73. Xproc timestr hour24 {
  74. X    set ampm [timeampm $hour24]
  75. X    case $hour24 in \
  76. X        {0 24} {return [format midnight]} \
  77. X        {12} {return [format noon]} \
  78. X        default {return [format "%d o'clock %s" [time12 $hour24] $ampm]} 
  79. X}
  80. X
  81. Xproc daysuffix monthday {
  82. X    case $monthday in \
  83. X        {1 21 31} {return st} \
  84. X        {2 22 32} {return nd} \
  85. X        {3 23 33} {return rd} \
  86. X        default {return th}
  87. X}
  88. X
  89. Xproc getload {} {
  90. X    global _load _load1 _load5 _load15
  91. X    
  92. X    scan [exec loadaverage] "%*d %*d %*d %f %f %f" _load1 _load5 _load15
  93. X
  94. X    set _load [format "%.2f (%.2f, %.2f)" $_load1 $_load5 $_load15]
  95. X
  96. X    set _load1  [format "%.2f" $_load1]
  97. X    set _load5  [format "%.2f" $_load5]
  98. X    set _load15 [format "%.2f" $_load15]
  99. X}
  100. X
  101. Xproc timecomment {hour24 minute} { 
  102. X    set hour12 [time12 $hour24]
  103. X    set ampm [timeampm $hour24]
  104. X
  105. X    proc ecomment {epsilon} {
  106. X        case $epsilon in \
  107. X            {0 1} {return "almost "} \
  108. X            {2 4} {return "around "} \
  109. X            {3}   {return ""}
  110. X    }
  111. X
  112. X    set fiveminute [expr {(($minute + 3) / 5) * 5}]
  113. X    set e [expr {($minute + 3) % 5}]
  114. X
  115. X    # first do the 15 minute intervals, then do the rest
  116. X
  117. X    if {$fiveminute == 0} \
  118. X        {return [format "%s%s" [ecomment $e] [timestr $hour24]]} \
  119. X    else { if {$fiveminute == 15} \
  120. X        {return [format "%squarter after %s (%s)" [ecomment $e] $hour12 $ampm]} \
  121. X    else { if {$fiveminute == 30} \
  122. X         {return [format "%shalf past %s (%s)" [ecomment $e] $hour12 $ampm]} \
  123. X    else { if {$fiveminute == 45} \
  124. X        {return [format "%squarter to %s (%s)" [ecomment $e] \
  125. X        [expr {($hour24 % 12) + 1}] $ampm]} \
  126. X    else { if {$fiveminute == 60} \
  127. X        {return [format "%s%s" [ecomment $e] \
  128. X        [timestr [expr {$hour24 + 1}]]]} \
  129. X    else {if {$minute < 30} \
  130. X        {return [format "%s%d after %s (%s)" [ecomment $e] \
  131. X        $fiveminute $hour12 $ampm]} \
  132. X        {return [format "%s%d to %s (%s)" [ecomment $e] \
  133. X        [expr {60 - $fiveminute}] [expr {($hour24 % 12) + 1}] $ampm]} \
  134. X    }}}}}
  135. X}
  136. X
  137. Xset fingercount 0
  138. X
  139. Xproc main {} {
  140. X    global _year _month _day _hour12 _hour24 _minute _second fingercount\
  141. X       _weekdaystring _monthstring
  142. X
  143. X    set fingercount [expr {$fingercount + 1}]
  144. X
  145. X    done
  146. X
  147. X#    echo -n Thesis: 
  148. X#    echo [fill 8 75 -h Computer Aided Colour Selection for People Who Have 
  149. X#    Better Things to Do With Their Time Than Play With Colour.]
  150. X
  151. X     # **** OLD Code, kept around for the heck of it, in case I reuse it ***
  152. X
  153. X    #set host [toupper [getenv SHORTHOST]]
  154. X    #getload
  155. X
  156. X    #echo -n Commentary:\ 
  157. X    #fill 12 75 -h It's $comment here in Waterloo. \  etc etc etc
  158. X
  159. X    #set netstat [exec grep "128.84.237" < [exec netstat -n]]
  160. X    #set netstat [exec netstat -n]
  161. X    #echo $netstat
  162. X    #set cornell [string match "* 128.84.237.*" $netstat]
  163. X    #echo $cornell
  164. X    #if {$cornell == 1} {echo "(Katy, you deadbeat, get to work!  Like me!)"}
  165. X    #if {$cornell >= 0} {echo $cornell [index $netstat $cornell]}
  166. X    
  167. X    #echo \nFortune #$fingercount:
  168. X    #echo [exec fortune -s]
  169. X
  170. X    set comment [format "Local Time: %s" [timecomment $_hour24 $_minute]]
  171. X    echo -n $comment
  172. X    set tabchars ""
  173. X    
  174. X    set commentlength [length $comment chars]
  175. X    set tabs [expr {(38 - $commentlength) / 8 + 1}]
  176. X    #echo $commentlength $tabs
  177. X    for {} {$tabs > 0} {set tabs [expr {$tabs - 1}]} \
  178. X        {set tabchars [format "%s\t" $tabchars]}
  179. X    
  180. X    echo [format "%s%s" $tabchars Date:] $_weekdaystring, the \
  181. X        [format "%d%s" $_day [daysuffix $_day]] of $_monthstring
  182. X    
  183. X#    set _sked [exec month -B1]
  184. X#    if {[length $_sked] > 0} \
  185. X#        { set temp1 [range $_sked 2 end] 
  186. X#          set length1 [expr {[length $temp1 chars] - 3}]
  187. X#          echo Schedule for today: \n "  " [range $temp1 0 $length1 chars] 
  188. X#        } \
  189. X#        { 
  190. X#          echo Nothing scheduled for today. 
  191. X#        }
  192. X    
  193. X#    echo \nAnswer this skill testing question and win valuable prizes!
  194. X#    echo \t\t\t ( $fingercount * \
  195. X#        [expr {(($_day * ($_second + 1)) * ($fingercount)) % 137 + 1}] + \
  196. X#    [expr {$_year % ($_second + 10) + 1}] ) / \
  197. X#    [expr {($_hour24 * $fingercount * ($_second + 1)) % 23 + 1}] 
  198. X
  199. X#     echo "\nCurrent State of the Thesis:"
  200. X#     echo [exec cat /u/bmacinty/.thesistoc]
  201. X#    echo " "
  202. X#    echo [fill 4 74 As my above finger info shows, I am not longer
  203. X#        gracing the halls of the DC.  I am now in the Big Apple,
  204. X#        doing a PhD at Columbia University.  My address is]
  205. X#    echo "\t435 W 119th St. Apt. 4B\n\tNew York, NY, 10027"
  206. X#    echo "\n\tPhone: 212-932-1911"
  207. X#    echo [fill 4 74 If you are in new York, phone me and we can get
  208. X#        together.]
  209. X#    echo " "
  210. X#
  211. X#    echo
  212. X#    echo [fill 0 65 I'll be at Cornell for the weekend.  If you 
  213. X#    need me, I'll be at (607) 277-1788.  Of course, I'll log on
  214. X#    as much as possible.]
  215. X
  216. X    echo \nRandom Space Filling Quotation:
  217. X    echo [exec fortune -s]
  218. X
  219. X    set cc [exec randterm dummyarg]
  220. X    # this shit to get around the ld.so: warning!
  221. X        if {([string match "ld.so:" [index $cc 0]]) == 1} {
  222. X        set cc [range $cc 9 end]
  223. X    }
  224. X    set colour1 [index $cc 3]
  225. X    set colour1v [format "(%d,%d,%d)"
  226. X        [index $cc 0] [index $cc 1] [index $cc 2]]
  227. X    set colour1f [index $cc 4]
  228. X
  229. X    set colour2 [index $cc 8]
  230. X    set colour2v [format "(%d,%d,%d)"
  231. X        [index $cc 5] [index $cc 6] [index $cc 7]]
  232. X    set colour2f [index $cc 9]
  233. X
  234. X    echo "Scientifically Selected Colour Scheme for the Aesthetically Challenged:"
  235. X    echo [fill 4 74 $colour1 $colour1v text on a field of 
  236. X        $colour2 $colour2v. This gives a luminance contrast of about 
  237. X        [index $cc 10]% on a standard NTSC display.]
  238. X    echo Try it out like this:
  239. X    echo [format "    xterm -fg %s -bg %s" $colour1f $colour2f ]
  240. X}
  241. X
  242. Xproc done {} {
  243. X    echo [ fill 2 75 I'm a Phd student in graphics, advised by Steven \
  244. X    Feiner.  This term's fun includes TAing CS4160, Computer Graphics. \
  245. X    Office hours are Tuesday and Wednesday, 11 till 12.  I'm also \
  246. X    thinking of taking the software qual.\n]
  247. X
  248. X    echo [ fill 2 75 I live in 1122D Fairchild right now, phone x43614.  \
  249. X    The phone number in the graphics lab x48699.\n]
  250. X
  251. X    echo [ fill 2 75 My home phone number is 222-5315, but don't bother \
  252. X    trying to reach me there, since I'm usually not at home.  \
  253. X    Email is your best bet.\n]
  254. X} 
  255. //go.sysin dd *
  256. echo x - .planinit2
  257. sed 's/^X//' > .planinit2 << '//go.sysin dd *'
  258. Xproc time12 hour24 {
  259. X    if {($hour24 % 12) == 0} \
  260. X        {return [format 12]} \
  261. X    else \
  262. X        {return [expr {$hour24 % 12}]}
  263. X}
  264. X
  265. Xproc timestr hour24 {
  266. X    case $hour24 in \
  267. X        {0 24} {return [format midnight]} \
  268. X        {12} {return [format noon]} \
  269. X        default {return [format "%d o'clock" [time12 $hour24]]} 
  270. X}
  271. X
  272. Xproc daysuffix monthday {
  273. X    case {$monthday} in \
  274. X        {1 21 31} {return st} \
  275. X        {2 22 32} {return nd} \
  276. X        {3 23 33} {return rd} \
  277. X        default {return th}
  278. X}
  279. X
  280. Xproc getload {} {
  281. X    global _load _load1 _load5 _load15
  282. X    
  283. X    scan [exec loadaverage] "%*d %*d %*d %f %f %f" _load1 _load5 _load15
  284. X
  285. X    set _load [format "%.2f (%.2f, %.2f)" $_load1 $_load5 $_load15]
  286. X
  287. X    set _load1  [format "%.2f" $_load1]
  288. X    set _load5  [format "%.2f" $_load5]
  289. X    set _load15 [format "%.2f" $_load15]
  290. X}
  291. X
  292. Xproc timecomment {hour24 minute} { 
  293. X    set hour12 [time12 $hour24]
  294. X
  295. X    proc ecomment {epsilon} {
  296. X        case $epsilon in \
  297. X            {0 1} {return "almost "} \
  298. X            {2 4} {return "around "} \
  299. X            {3}   {return ""}
  300. X    }
  301. X
  302. X    set fiveminute [expr {(($minute + 3) / 5) * 5}]
  303. X    set e [expr {($minute + 3) % 5}]
  304. X
  305. X    # first do the 15 minute intervals, then do the rest
  306. X
  307. X    if {$fiveminute == 0} \
  308. X        {return [format "%s%s" [ecomment $e] [timestr $hour24]]} \
  309. X    else { if {$fiveminute == 15} \
  310. X        {return [format "%squarter after %s" [ecomment $e] $hour12]} \
  311. X    else { if {$fiveminute == 30} \
  312. X         {return [format "%shalf past %s" [ecomment $e] $hour12]} \
  313. X    else { if {$fiveminute == 45} \
  314. X        {return [format "%squarter to %s" [ecomment $e] \
  315. X        [expr {($hour24 % 12) + 1}]]} \
  316. X    else { if {$fiveminute == 60} \
  317. X        {return [format "%s%s" [ecomment $e] \
  318. X        [timestr [expr {$hour24 + 1}]]]} \
  319. X    else {if {$minute < 30} \
  320. X        {return [format "%s%d after %s" [ecomment $e] \
  321. X        $fiveminute $hour12]} \
  322. X        {return [format "%s%d to %s" [ecomment $e] \
  323. X        [expr {60 - $fiveminute}] [expr {($hour24 % 12) + 1}]]} \
  324. X    }}}}}
  325. X}
  326. X
  327. Xproc main {} {
  328. X    global _year _month _day _hour12 _hour24 _minute _second _fingercount \
  329. X       _weekdaystring _monthstring
  330. X    echo To actually do some work this term ... and keep my plan up to date!
  331. X    echo Skill Testing Question: \
  332. X    [expr {$_year % $_fingercount + 1}] * \
  333. X    [expr {($_day * ($_second + 1) * ($_month + 10) * ($_minute + 1)) % $_fingercount + 1}] + \
  334. X    [expr {$_year % ($_second + 1) + 1}] / \
  335. X    [expr {(($_hour24 * $_fingercount) % $_second) + 1}]
  336. X    
  337. X    #for {set i 0} {$i < 60} {set i [expr {$i+1}]} {echo [timecomment 4 $i]}
  338. X    #set comment [timecomment $_hour24 $_minute]
  339. X    #set host [toupper [getenv SHORTHOST]]
  340. X    #getload
  341. X    
  342. X    #echo -n Commentary:\ 
  343. X    #fill 12 75 -h It's $comment here in Waterloo. \  etc etc etc
  344. X    
  345. X    set comment [format "Local Time: %s" [timecomment $_hour24 $_minute]]
  346. X    echo -n $comment
  347. X    set tabchars ""
  348. X    
  349. X    set fred [length $comment chars]
  350. X    set tabs [expr {(38 - $fred) / 8 + 1}]
  351. X    #echo $fred $tabs
  352. X    for {} {$tabs > 0} {set tabs [expr {$tabs - 1}]} \
  353. X    {set tabchars [format "%s\t" $tabchars]}
  354. X    
  355. X    echo [format "%s%s" $tabchars Date:] $_weekdaystring, the \
  356. X    [format "%d%s" $_day [daysuffix $_day]] of $_monthstring
  357. X    
  358. X    set _sked [exec month -B1]
  359. X    if {[length $_sked] > 0} \
  360. X    { set temp1 [range $_sked 2 end] 
  361. X    set length1 [expr {[length $temp1 chars] - 3}]
  362. X    echo Schedule for today: \n "  " [range $temp1 0 $length1 chars] 
  363. X    } \
  364. X    { echo Nothing scheduled for today. }
  365. X    
  366. X    #set netstat [exec grep "128.84.237" < [exec netstat -n]]
  367. X    set netstat [exec netstat -n]
  368. X    #echo $netstat
  369. X    set cornell [string match "* 128.84.237.*" $netstat]
  370. X    #echo $cornell
  371. X    if {$cornell == 1} {echo "(Katy, you deadbeat, get to work!  Like me!)"}
  372. X    #if {$cornell >= 0} {echo $cornell [index $netstat $cornell]}
  373. X}
  374. //go.sysin dd *
  375. echo x - Makefile
  376. sed 's/^X//' > Makefile << '//go.sysin dd *'
  377. XTCLOBJS = plantcl.o tcl_cmds.o 
  378. X#uniqueproc.o
  379. XCC = gcc
  380. XCFLAGS = -O -g -I${TCLINCLUDE} -Wall
  381. XTCLLIBSDIR = /u/foria/bm/lib
  382. XTCLINCLUDE = /u/foria/bm/include
  383. X
  384. Xtclplan : ${TCLOBJS}
  385. X    gcc -o tclplan ${CFLAGS} ${TCLOBJS} -L${TCLLIBSDIR} -ltcl
  386. X
  387. Xlexplan : ${OLDOBJS}
  388. X    gcc -o lexplan ${CFLAGS} ${OLDOBJS}
  389. X
  390. Xplanmain.o : plan.h
  391. X
  392. Xplantcl.o : plantcl.c tcl_cmds.h plan_proto.h ${TCLINCLUDE}/tcl.h
  393. X
  394. Xuniqueproc.o : uniqueproc.c
  395. X
  396. Xtcl_cmds.o : tcl_cmds.c ${TCLINCLUDE}/tcl.h
  397. X
  398. Xlex.yy.o : lex.yy.c plan.h 
  399. X
  400. Xlex.yy.c : plan.l
  401. X    flex -i plan.l
  402. X
  403. Xclean:
  404. X    rm -f ${TCLOBJS} ${OLDOBJS} tclplan lexplan
  405. //go.sysin dd *
  406. echo x - plan_proto.h
  407. sed 's/^X//' > plan_proto.h << '//go.sysin dd *'
  408. X#include <sys/types.h>
  409. X#include <sys/stat.h>
  410. X#include <sys/time.h>
  411. X#include <sys/file.h>
  412. X#include <sgtty.h>
  413. X#include <signal.h>
  414. X#include <errno.h>
  415. X#include <string.h>
  416. X#include <ctype.h>
  417. X#include "tcl.h"
  418. X
  419. Xextern int puts();
  420. Xextern int stat(char*, struct stat*);
  421. X
  422. Xextern char     getopt(int, char**, const char*);
  423. Xextern char    *getenv(const char*);
  424. Xextern int      chdir(const char*);
  425. Xextern int      unlink(const char*);
  426. Xextern int      mknod(const char*, int, int);
  427. Xextern int      access(const char*, int);
  428. Xextern int      select(int, fd_set*,fd_set*,fd_set*,struct timeval*);
  429. Xextern int      fprintf(FILE*, char*,...);
  430. X#if !defined(sun)
  431. Xextern int      umask(int);
  432. X#endif SUN4_1
  433. Xextern int      fclose(FILE*);
  434. Xextern int      getppid(void), getpid(void);
  435. Xextern int      getpgrp(int);
  436. Xextern FILE     *popen(const char*, const char*);
  437. Xextern int      pclose(FILE*), fscanf(FILE*,const char*,...);
  438. Xextern int      fork(void);
  439. Xextern int      setpgrp(int,int);
  440. Xextern int      ioctl(int,int,void*);
  441. Xextern int      close(int);
  442. Xextern void     perror(const char*);
  443. Xextern void     fputs(const char*, FILE*);
  444. Xextern void     sleep(unsigned int);
  445. Xextern void     bzero(void*,int); 
  446. Xextern int      fread(void*,unsigned,unsigned,FILE*);
  447. Xextern int      dup2(int,int);
  448. Xextern int      open(char*, int,int);
  449. X
  450. Xextern int      unique_proc();
  451. //go.sysin dd *
  452. echo x - plantcl.c
  453. sed 's/^X//' > plantcl.c << '//go.sysin dd *'
  454. X/*                               -*- Mode: c -*- 
  455. X * plantcl.c -- the mainline for my tcl-based dynamic plan program
  456. X * Author          : Blair MacIntyre
  457. X * Last Modified By: Blair MacIntyre
  458. X * Last Modified On: Wed Apr 15 13:49:59 1992
  459. X * Update Count    : 446
  460. X * Status          : Version 3.
  461. X * 
  462. X * $Source: /u/foria/bm/src/plan/RCS/plantcl.c,v $
  463. X * $Date: 1992/04/15 17:50:44 $
  464. X * $Author: bm $
  465. X * $Revision: 1.12 $
  466. X */
  467. X
  468. X/*
  469. X**  A process to create dynamic .plan files.  Two programs in one:
  470. X**  1) Creates a fifo, waits for someone to connect to it.  Redo .plan info
  471. X**     each time.
  472. X**  2) Runs in the background and rewrites the plan file evey N seconds.
  473. X**     Needed for NFS environments, since fifo's can't be read over NFS.
  474. X**
  475. X** Original version:  Rich Salz
  476. X** Prints out the time, and a guess-timate of the temperature,
  477. X** based on the day of the year and the time.
  478. X**
  479. X** Major Revision:  (Blair MacIntyre, Oct 1, 1990)
  480. X** Accepts a configuration file
  481. X**
  482. X** Major Revision:  (Blair MacIntyre, April 11, 1992)
  483. X** Doesn't have to use a fifo.  Seems like a step backwards toward the dark
  484. X** ages, but they use NFS for everything here at Columbia.
  485. X*/
  486. X
  487. X#include <stdio.h>
  488. X#include <stdlib.h>
  489. X#include <sys/file.h>
  490. X#include "plan_proto.h"
  491. X#include "tcl_cmds.h"
  492. X
  493. X/* beats the heck out of me why these aren't prototyped */
  494. Xint rename(char*, char*);
  495. Xint flock(int, int);
  496. X
  497. X/* default FIFO name and initialization files */
  498. X#define PLAN ".plan"
  499. X#define PLANINIT ".planinit"
  500. X
  501. X/* various defines for variable contents */
  502. X#define TIME_DEFAULT "%2d:%02d:%02d"
  503. X#define DATE_DEFAULT "%d %.3s %d"
  504. X#define INFO_DEFAULT "proc main {} {\n\
  505. X    global _time _date\n\
  506. X    echo \"I've got the time ....\"\n\
  507. X        echo \"Time:       $_time \t\t\tDate: $_date\"\n\
  508. X    }\n"
  509. X#define INFO_ERROR "proc main {} {\n\
  510. X    global _time _date errorInfo\n\
  511. X    echo \"      Plan init file contained an error near:\"\n\
  512. X        echo $errorInfo\n\
  513. X    echo \"Time:       $_time \t\t\tDate: $_date\"\n\
  514. X    }\n"
  515. X#define INFO_READ_ERROR "proc main {} {\n\
  516. X    global _time _date\n\
  517. X    echo \"      Error Reading plan init file.\"\n\
  518. X    echo \"Time:       $_time \t\t\tDate: $_date\"\n\
  519. X    }\n"
  520. X#define INFO_NOMAIN "proc main {} {\n\
  521. X    global _time _date\n\
  522. X    echo \"      Plan init file did not define a 'main' procedure.\"\n\
  523. X    echo \"Time:       $_time \t\t\tDate: $_date\"\n\
  524. X    }\n"
  525. X
  526. X#define INFO_NO_DONE "proc done {} {\n\
  527. X        echo \"Plan?  Plan?  We don't need no stinking plan!\"\
  528. X    }\n"
  529. X
  530. X#ifndef FIFO
  531. X/* the default amount of time to sleep between plan file recreates (2 minues)*/
  532. X#define DEFAULT_SLEEP_TIME (120)
  533. X#endif
  534. X
  535. X/* global variables */
  536. Xstatic char *fifo = NULL;
  537. Xstatic char  lockname[255];
  538. Xstatic char* tempfile;
  539. Xstatic char  lockfd;
  540. X
  541. XFILE *FingerOutput;        /* needed in tcl_cmds.c */
  542. X
  543. Xstatic Tcl_Interp *my_interp;
  544. Xstatic void Setup();
  545. Xstatic Tcl_Interp *GetConfig(char *, int);
  546. Xstatic void SetState(Tcl_Interp *interp);
  547. X
  548. X/***********************************************************************
  549. X * general Exit routine 
  550. X ***********************************************************************/
  551. Xint Exit(void)
  552. X{
  553. X    int fd;
  554. X    int         result;
  555. X
  556. X    /* Close and remove the current FingerOutput tempfile */
  557. X    (void)fclose(FingerOutput);
  558. X    if (unlink(tempfile) < 0 && errno != ENOENT) {
  559. X        perror("Can't unlink tempfile");
  560. X    }
  561. X
  562. X    /* Remove the lockfile */
  563. X    close(lockfd);
  564. X    if (unlink(lockname) < 0 && errno != ENOENT) {
  565. X        perror("Can't unlink lockfile");
  566. X    }
  567. X
  568. X#ifdef FIFO
  569. X    /* Remove the FIFO. */
  570. X    if (unlink(fifo) < 0 && errno != ENOENT) {
  571. X        perror("Can't unlink");
  572. X    exit(1);
  573. X    }
  574. X#endif
  575. X
  576. X    /* Open the fifo for writing. Attempt to create a simple file, so there
  577. X       is something reasonable left lying around when we are gone */
  578. X    if ((FingerOutput = fopen(fifo, "w")) == NULL) {
  579. X    perror("Can't fopen");
  580. X    exit(1);
  581. X    }
  582. X    fd = fileno(FingerOutput);
  583. X
  584. X    /* execute the TCL "done" procedure to (presumably) dump a final message */
  585. X    SetState(my_interp);
  586. X    result = Tcl_Eval(my_interp, "done", 0, NULL);
  587. X    if (result == TCL_OK) {
  588. X    if (*my_interp->result != 0) {
  589. X        fprintf(FingerOutput, "TCL Result: %s\n", my_interp->result);
  590. X    }
  591. X    } else {
  592. X    if (result == TCL_ERROR) {
  593. X        fprintf(FingerOutput, "TCL Error");
  594. X    } else {
  595. X        fprintf(FingerOutput, "TCL Error %d", result);
  596. X    }
  597. X    if (*my_interp->result != 0) {
  598. X        fprintf(FingerOutput, ": %s\n", my_interp->result);
  599. X    } else {
  600. X        fprintf(FingerOutput, "\n");
  601. X    }
  602. X    }
  603. X
  604. X    /* Close it */
  605. X    (void)fclose(FingerOutput);
  606. X
  607. X    exit(0);
  608. X    /*NOTREACHED*/
  609. X    return 0;
  610. X}
  611. X
  612. X/***********************************************************************
  613. X * read the config file into Config_buffer and Tcl_Eval it.
  614. X ***********************************************************************/
  615. XTcl_Interp *GetConfig(char *initfile, int filesize)
  616. X{
  617. X    int count, result;
  618. X    FILE *ConfigFile = fopen(initfile, "r"); 
  619. X    Tcl_Interp *interp = NULL;
  620. X    char *Config_buffer = NULL;
  621. X    
  622. X    /* 
  623. X     * initialize the TCL interpretter and add our custom functions 
  624. X     */
  625. X    interp = Tcl_CreateInterp();
  626. X    Tcl_CreateCommand(interp, "getenv", cmdGetEnv, (ClientData) 0, 0);
  627. X    Tcl_CreateCommand(interp, "echo", cmdEcho, (ClientData) 0, 0);
  628. X    Tcl_CreateCommand(interp, "toupper", cmdToUpper, (ClientData) 0, 0);
  629. X    Tcl_CreateCommand(interp, "tolower", cmdToLower, (ClientData) 0, 0);
  630. X    Tcl_CreateCommand(interp, "fill", cmdFill, (ClientData) 0, 0);
  631. X    
  632. X    /* 
  633. X     * we must do this so that our variables are always defined, even the
  634. X     * first time the init file is executed
  635. X     */
  636. X    SetState(interp);
  637. X
  638. X    /* use default plan */
  639. X    if (!ConfigFile) {
  640. X    (void)Tcl_Eval(interp, INFO_DEFAULT, 0, NULL);
  641. X    (void)Tcl_Eval(interp, INFO_NO_DONE, 0, NULL);
  642. X    }
  643. X    else
  644. X    {
  645. X    Config_buffer = malloc(filesize+1);
  646. X    count = fread(Config_buffer, sizeof(char), filesize, ConfigFile);
  647. X    if (count == 0) 
  648. X    {
  649. X        (void)Tcl_Eval(interp, INFO_READ_ERROR, 0, NULL);
  650. X        (void)Tcl_Eval(interp, INFO_NO_DONE, 0, NULL);
  651. X    }
  652. X    else
  653. X    {    
  654. X        Config_buffer[count] = '\0';
  655. X        result = Tcl_Eval(interp, Config_buffer, 0, NULL);
  656. X        if (result == TCL_OK) {
  657. X        /* check for main */
  658. X        result = Tcl_Eval(interp, "info procs main", 0, NULL);
  659. X        
  660. X        if (result == TCL_OK && strcmp(interp->result,"main") != 0) {
  661. X            /* no main procedure defined */
  662. X            (void)Tcl_Eval(interp, INFO_NOMAIN, 0, NULL);
  663. X        }
  664. X
  665. X        /* check for done */
  666. X        result = Tcl_Eval(interp, "info procs done", 0, NULL);
  667. X        
  668. X        if (result == TCL_OK && strcmp(interp->result,"done") != 0) {
  669. X            /* no done procedure defined */
  670. X            (void)Tcl_Eval(interp, INFO_NO_DONE, 0, NULL);
  671. X        }
  672. X        } else {
  673. X        (void)Tcl_Eval(interp, INFO_ERROR, 0, NULL);
  674. X        (void)Tcl_Eval(interp, INFO_NO_DONE, 0, NULL);
  675. X        }
  676. X    }
  677. X
  678. X    free(Config_buffer);
  679. X    fclose(ConfigFile);
  680. X    
  681. X    } /* if */
  682. X
  683. X    return interp;
  684. X}
  685. X
  686. X/***********************************************************************
  687. X * This function sets up signal handlers for the HUP and TERM signals.
  688. X * The signal handlers remove the named pipe and exit, rewriting a simple
  689. X * .plan on exit.
  690. X * This function sets up ignoring of INT, PIPE and QUIT signals.
  691. X * It also ensure that the forked process is in the process group of 
  692. X * the parent.
  693. X ***********************************************************************/
  694. Xvoid Setup()
  695. X{
  696. X    int pid,fd;
  697. X
  698. X    /*
  699. X     * Arrange for handling or ignoring of signals
  700. X     */
  701. X    signal( SIGTERM, Exit );
  702. X    signal( SIGPIPE, SIG_IGN );
  703. X    signal( SIGHUP, SIG_IGN );
  704. X    signal( SIGINT, SIG_IGN );
  705. X    signal( SIGQUIT, SIG_IGN );
  706. X
  707. X    /* fork.. the parent dies, the child lives on */
  708. X#ifndef DEBUG
  709. X    switch( fork() )
  710. X    {
  711. X      case -1:            /* fail */
  712. X    /* fork failed */
  713. X    perror( "fork" );
  714. X    Exit();
  715. X    break;
  716. X
  717. X      case 0:            /* child */
  718. X#ifdef FIFO
  719. X    /*
  720. X     * If the FIFO exists, unlink it.
  721. X     */
  722. X    if (access(fifo,F_OK) == 0)
  723. X    {
  724. X        (void)unlink(fifo);
  725. X    }
  726. X
  727. X    (void)umask(0);
  728. X    if (mkfifo(fifo, (mode_t)0644) < 0)
  729. X    {
  730. X        perror("Can't mknod");
  731. X        Exit();
  732. X    }
  733. X#else
  734. X    /* see if we can write to the .plan file */
  735. X    if (access(fifo,W_OK))
  736. X    {
  737. X        if (errno != ENOENT)
  738. X        {
  739. X        perror("can't write to file");
  740. X        Exit();
  741. X        }
  742. X    }
  743. X#endif
  744. X    pid = getpid();
  745. X
  746. X    if( setpgrp(0,pid) < 0 ) 
  747. X    {
  748. X        perror( "setpgrp" );
  749. X        Exit();
  750. X    }
  751. X
  752. X    /* disassociate controlling terminal */
  753. X    if( (fd = open( "/dev/tty", O_RDONLY, 0 ) ) == -1 ) {
  754. X        perror( "open /dev/tty" );
  755. X        Exit();
  756. X    }
  757. X
  758. X    if( ioctl( fd, TIOCNOTTY, 0 ) == -1 ) {
  759. X        perror( "ioctl" );
  760. X    }
  761. X
  762. X    close( fd );
  763. X
  764. X    /* 
  765. X     * close the I/O files opened by C and redirect them to /dev/null 
  766. X     * for TCL.
  767. X     */
  768. X    close(0); 
  769. X    close(1);
  770. X    close(2);
  771. X
  772. X    if (fd = open("/dev/null", O_RDWR, 0) == -1)
  773. X    {
  774. X        perror( "open /dev/null");
  775. X        Exit();
  776. X    }
  777. X
  778. X    if (dup2(fd,0) == -1)
  779. X    {
  780. X        perror( "dup2 stdin");
  781. X        Exit();
  782. X    }
  783. X
  784. X    if (dup2(fd,1) == -1)
  785. X    {
  786. X        perror( "dup2 stdout");
  787. X        Exit();
  788. X    }
  789. X
  790. X    if (dup2(fd,2) == -1)
  791. X    {
  792. X        perror( "dup2 stderr");
  793. X        Exit();
  794. X    }
  795. X
  796. X    return;
  797. X      default:            /* parent */
  798. X    exit(0);
  799. X    }
  800. X#endif
  801. X}
  802. X
  803. X/**********************************************************************
  804. X * month and day variables for setting the TCL variables
  805. X **********************************************************************/
  806. Xchar *months[12] = { "January", "Febuary", "March", "April", "May", "June",
  807. X             "July", "August", "September", "October", 
  808. X             "November", "December" };
  809. Xchar *dayofweek[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
  810. X             "Friday", "Saturday" };
  811. X
  812. X/**********************************************************************
  813. X * the good stuff:  set the TCL variables!
  814. X **********************************************************************/
  815. Xvoid SetState(Tcl_Interp *interp)
  816. X{
  817. X    time_t time(time_t*), curr; 
  818. X    struct tm parts;
  819. X
  820. X    char F[BUFSIZ];        /* temporary buffer.  the name is historical */
  821. X
  822. X    curr = time(0);
  823. X    parts = *localtime(&curr);
  824. X
  825. X#ifdef DEBUG
  826. X#ifdef FIFO
  827. X    fprintf(stderr, "Someone is fingering me!\n");
  828. X#endif
  829. X#endif
  830. X    
  831. X    sprintf( F, TIME_DEFAULT, parts.tm_hour, parts.tm_min, parts.tm_sec);
  832. X    Tcl_SetVar(interp, "_time", F, 1);
  833. X
  834. X    sprintf( F, "%d", parts.tm_hour);
  835. X    Tcl_SetVar(interp, "_hour24", F, 1);
  836. X
  837. X    if (parts.tm_hour == 0)
  838. X    sprintf( F, "12");
  839. X    else if (parts.tm_hour < 13)
  840. X    sprintf( F, "%d", parts.tm_hour);
  841. X    else
  842. X    sprintf( F, "%d", parts.tm_hour - 12);
  843. X    Tcl_SetVar(interp, "_hour12", F, 1);
  844. X
  845. X    sprintf( F, "%d", parts.tm_min);
  846. X    Tcl_SetVar(interp, "_minute", F, 1);
  847. X
  848. X    sprintf( F, "%d", parts.tm_sec);
  849. X    Tcl_SetVar(interp, "_second", F, 1);
  850. X
  851. X    sprintf( F, DATE_DEFAULT, parts.tm_mday, months[parts.tm_mon], 
  852. X        parts.tm_year + 1900 );
  853. X    Tcl_SetVar(interp, "_date", F, 1);
  854. X
  855. X    sprintf( F, "%d", parts.tm_mday);
  856. X    Tcl_SetVar(interp, "_day", F, 1);
  857. X
  858. X    sprintf( F, "%d", parts.tm_mon + 1); 
  859. X    Tcl_SetVar(interp, "_month", F, 1);
  860. X
  861. X    sprintf( F, "%s", months[parts.tm_mon]);
  862. X    Tcl_SetVar(interp, "_monthstring", F, 1);
  863. X
  864. X    sprintf( F, "%d", parts.tm_year + 1900 );
  865. X    Tcl_SetVar(interp, "_year", F, 1);
  866. X
  867. X    sprintf( F, "%d", parts.tm_wday );
  868. X    Tcl_SetVar(interp, "_weekday", F, 1);
  869. X
  870. X    sprintf( F, "%d", parts.tm_yday );
  871. X    Tcl_SetVar(interp, "_yearday", F, 1);
  872. X
  873. X    sprintf( F, "%s", dayofweek[parts.tm_wday]);
  874. X    Tcl_SetVar(interp, "_weekdaystring", F, 1);
  875. X}
  876. X
  877. X/***********************************************************************
  878. X * the mainline, where the work is done
  879. X ***********************************************************************/
  880. Xvoid 
  881. Xmain(int argc, char *argv[])
  882. X{
  883. X    int        fd;
  884. X#ifdef FIFO
  885. X    fd_set    writers;
  886. X#endif
  887. X    struct stat stat_buf;
  888. X    time_t      mod_time;
  889. X    int         result;
  890. X    int         c;
  891. X    extern char *optarg;
  892. X    char        *initfile = NULL, *directory = NULL;
  893. X#ifndef FIFO
  894. X    int         sleeptime = 0;
  895. X#endif
  896. X    char        tempheader[6];
  897. X
  898. X#ifdef FIFO
  899. X    while ((c = getopt (argc, argv, "f:i:d:")) != EOF)
  900. X#else
  901. X    while ((c = getopt (argc, argv, "f:i:d:s:")) != EOF)
  902. X#endif
  903. X    switch (c) 
  904. X    {
  905. X      case 'f':
  906. X        fifo = optarg;
  907. X        break;
  908. X      case 'i':
  909. X        initfile = optarg;
  910. X        break;
  911. X      case 'd':
  912. X        directory = optarg;
  913. X        break;
  914. X#ifndef FIFO
  915. X      case 's':
  916. X        sleeptime = atoi(optarg);
  917. X        break;
  918. X
  919. X      case '?':
  920. X        fprintf(stderr,"usage: %s [-f outputfile] [-i initfile] [-d dir] [-s sleeptime]\n",
  921. X             argv[0]);
  922. X#else
  923. X      case '?':
  924. X        fprintf (stderr,"usage: %s [-f fifofile] [-i initfile] [-d dir]\n",
  925. X             argv[0]);
  926. X#endif
  927. X        exit(1);
  928. X    }
  929. X
  930. X    /* use defaults if the options aren't specified */
  931. X    if (!fifo)
  932. X    fifo = PLAN;
  933. X    if (!initfile)
  934. X    initfile = PLANINIT;
  935. X    if (sleeptime == 0)
  936. X    sleeptime = DEFAULT_SLEEP_TIME;
  937. X
  938. X    /* Go to the right directory. */
  939. X    if (!directory && ((directory = getenv("HOME")) == NULL))
  940. X    {
  941. X    fprintf(stderr, "No $HOME.\n");
  942. X    exit(1);
  943. X    }
  944. X
  945. X    if (chdir(directory) < 0) {
  946. X    perror("Can't cd home directory");
  947. X    exit(1);
  948. X    }
  949. X
  950. X    /*
  951. X     * Check for existence of the FIFO, and
  952. X     * that the current "plan" program isn't running.
  953. X     * Setup signal handlers for HUP and TERM signals, 
  954. X     *    to ignore INT and QUIT, and fork
  955. X     */
  956. X    stat(initfile, &stat_buf);
  957. X    mod_time = stat_buf.st_mtime;
  958. X    FingerOutput = stdout;
  959. X
  960. X    /* lock out lockfile */
  961. X    sprintf(lockname, "%s.lock", fifo);
  962. X    if ((lockfd = open(lockname, O_RDONLY | O_CREAT, 0600)) < 0)
  963. X    {
  964. X    perror("Can't open lockfile");
  965. X    exit(1);
  966. X    }
  967. X    if (flock(lockfd, LOCK_EX | LOCK_NB))
  968. X    {
  969. X    fprintf(stderr, "%s already running for %s\n", argv[0], fifo);
  970. X    exit(1);
  971. X    }
  972. X
  973. X#ifndef FIFO
  974. X    /* get a temporary name */
  975. X    strncpy(tempheader, fifo, 5);
  976. X    tempheader[5] = '\0';
  977. X    tempfile = tempnam (".", tempheader);
  978. X#endif
  979. X
  980. X    /* do the initial setup */
  981. X    Setup();
  982. X
  983. X    /*
  984. X     * read in the config file
  985. X     * This initializes the TCL interpreter as well.
  986. X     * If the config file cannot be read, or there are
  987. X     * TCL errors when eval'ing it, a default "main" procedure
  988. X     * is set up.
  989. X     */
  990. X    my_interp = GetConfig(initfile, stat_buf.st_size);
  991. X
  992. X    /* Enter the server loop. */
  993. X    for (;;) {
  994. X
  995. X    /* Open the fifo for writing. */
  996. X#ifdef FIFO
  997. X    if ((FingerOutput = fopen(fifo, "w")) == NULL) {
  998. X#else
  999. X    if ((FingerOutput = fopen(tempfile, "w")) == NULL) {
  1000. X#endif
  1001. X        perror("Can't fopen");
  1002. X        Exit();
  1003. X    }
  1004. X    fd = fileno(FingerOutput);
  1005. X
  1006. X#ifdef FIFO
  1007. X    /* Wait until someone else opens it for reading, so that we can
  1008. X     * write on it. */
  1009. X    FD_ZERO(&writers);
  1010. X    FD_SET(fd, &writers);
  1011. X    if (select(fd + 1, (fd_set *)NULL, &writers, (fd_set *)NULL,
  1012. X        (struct timeval *)NULL) < 0) {
  1013. X        if (errno != EINTR)
  1014. X        perror("Can't select");
  1015. X        continue;
  1016. X    }
  1017. X    if (!FD_ISSET(fd, &writers))
  1018. X        /* "Can't happen" */
  1019. X        continue;
  1020. X#endif
  1021. X    /* if the config file has changed, reread it */
  1022. X    stat(initfile, &stat_buf);
  1023. X    if (mod_time != stat_buf.st_mtime)
  1024. X    {
  1025. X        mod_time = stat_buf.st_mtime;
  1026. X        /* 
  1027. X         * re-initialize the TCL interpreter from the config file.
  1028. X         */
  1029. X        Tcl_DeleteInterp(my_interp);
  1030. X        my_interp = GetConfig(initfile, stat_buf.st_size);
  1031. X    }
  1032. X
  1033. X    /* execute the TCL "main" procedure to (presumably) dump the message */
  1034. X    SetState(my_interp);
  1035. X    result = Tcl_Eval(my_interp, "main", 0, NULL);
  1036. X    if (result == TCL_OK) {
  1037. X        if (*my_interp->result != 0) {
  1038. X        fprintf(FingerOutput, "TCL Result: %s\n", my_interp->result);
  1039. X        }
  1040. X    } else {
  1041. X        if (result == TCL_ERROR) {
  1042. X        fprintf(FingerOutput, "TCL Error");
  1043. X        } else {
  1044. X        fprintf(FingerOutput, "TCL Error %d", result);
  1045. X        }
  1046. X        if (*my_interp->result != 0) {
  1047. X        fprintf(FingerOutput, ": %s\n", my_interp->result);
  1048. X        } else {
  1049. X        fprintf(FingerOutput, "\n");
  1050. X        }
  1051. X    }
  1052. X
  1053. X    /* Close it so they stop reading, and pause to make sure of it. */
  1054. X    (void)fclose(FingerOutput);
  1055. X
  1056. X#ifndef FIFO
  1057. X    /* move the temp file to the fifo */
  1058. X    if (rename(tempfile, fifo))
  1059. X    {
  1060. X        perror("error renaming tempfile to output file");
  1061. X        Exit();
  1062. X    }
  1063. X#endif
  1064. X#ifdef FIFO
  1065. X    (void)sleep(5);
  1066. X#else
  1067. X    (void)sleep(sleeptime);
  1068. X#endif
  1069. X    }
  1070. X
  1071. X    /* NOTREACHED */
  1072. X}
  1073. X
  1074. X/* Local Variables: */
  1075. X/* compile-command: "make" */
  1076. X/* End: */
  1077. //go.sysin dd *
  1078. echo x - tcl_cmds.c
  1079. sed 's/^X//' > tcl_cmds.c << '//go.sysin dd *'
  1080. X/*
  1081. X**  A process to create dynamic .plan files.  Creates a fifo, waits for
  1082. X**  someone to connect to it.  Optional first argument is the directory
  1083. X**  to chdir(2) to, as in "plan ~rsalz &"; default is value of $HOME.
  1084. X**
  1085. X** Original version:
  1086. X** Prints out the time, and a guess-timate of the temperature,
  1087. X** based on the day of the year and the time.
  1088. X**
  1089. X** Major Revision:  (Blair MacIntyre, Oct 1, 1990)
  1090. X** Accepts a configuration file
  1091. X**
  1092. X** Major Revision 2:  (Blair MacIntyre, Nov 1, 1990)
  1093. X** Uses TCL to interpret the configuration file
  1094. X*/
  1095. X
  1096. X#include <stdio.h>
  1097. X#include <stdlib.h>
  1098. X#include <string.h> 
  1099. X#include <ctype.h>
  1100. X#include "tcl.h"
  1101. X
  1102. Xextern char    *getenv(const char*);
  1103. Xextern int      fputc(int, FILE*);
  1104. X#if defined(ultrix) | defined(sun)
  1105. Xextern int      toupper(int);
  1106. Xextern int      tolower(int);
  1107. X#endif
  1108. Xextern int      fprintf(FILE*, char*,...);
  1109. X
  1110. Xextern FILE *FingerOutput;
  1111. X
  1112. Xint
  1113. XcmdToUpper( clientData, interp, argc, argv )
  1114. X    char *clientData;
  1115. X    Tcl_Interp *interp;
  1116. X    int argc;
  1117. X    char **argv;
  1118. X{
  1119. X    int j;
  1120. X    char *buffer;
  1121. X
  1122. X    if (argc != 2) {
  1123. X    sprintf(interp->result,
  1124. X        "incorrect number of parameters to \"%s\" command", argv[0]);
  1125. X    return (TCL_ERROR);
  1126. X    }
  1127. X
  1128. X    if ((buffer = malloc(strlen(argv[1]) + 1)) == NULL) {
  1129. X    sprintf(interp->result,
  1130. X        "out of memory while executing \"%s\" command", argv[0]);
  1131. X    return (TCL_ERROR);
  1132. X    }
  1133. X    
  1134. X    for (j = 0; argv[1][j] != NULL; j++ )
  1135. X#if defined(ultrix)
  1136. X    buffer[j] = toupper(argv[1][j]);
  1137. X#else
  1138. X        buffer[j] = islower( argv[1][j] ) ? toupper( argv[1][j] ) : argv[1][j];
  1139. X#endif
  1140. X    buffer[j] = 0;
  1141. X
  1142. X    Tcl_Return(interp, buffer, TCL_DYNAMIC);
  1143. X
  1144. X    return (TCL_OK);
  1145. X}
  1146. X
  1147. Xint
  1148. XcmdToLower( clientData, interp, argc, argv )
  1149. X    char *clientData;
  1150. X    Tcl_Interp *interp;
  1151. X    int argc;
  1152. X    char **argv;
  1153. X{
  1154. X    int j;
  1155. X    char *buffer;
  1156. X
  1157. X    if (argc != 2) {
  1158. X    sprintf(interp->result,
  1159. X        "incorrect number of parameters to \"%s\" command", argv[0]);
  1160. X    return (TCL_ERROR);
  1161. X    }
  1162. X
  1163. X    if ((buffer = malloc(strlen(argv[1]) + 1)) == NULL) {
  1164. X    sprintf(interp->result,
  1165. X        "out of memory while executing \"%s\" command", argv[0]);
  1166. X    return (TCL_ERROR);
  1167. X    }
  1168. X    
  1169. X    for (j = 0; argv[1][j] != NULL; j++ )
  1170. X#if defined(ultrix)
  1171. X    buffer[j] = tolower(argv[1][j]);
  1172. X#else
  1173. X        buffer[j] = isupper( argv[1][j] ) ? tolower( argv[1][j] ) : argv[1][j];
  1174. X#endif
  1175. X    buffer[j] = 0;
  1176. X
  1177. X    Tcl_Return(interp, buffer, TCL_DYNAMIC);
  1178. X
  1179. X    return (TCL_OK);
  1180. X}
  1181. X
  1182. Xint
  1183. XcmdGetEnv(clientData, interp, argc, argv)
  1184. X    char *clientData;
  1185. X    Tcl_Interp *interp;
  1186. X    int argc;
  1187. X    char **argv;
  1188. X{
  1189. X    if (argc != 2) {
  1190. X    sprintf(interp->result, 
  1191. X        "incorrect number of parameters to \"%s\" command", argv[0]);
  1192. X    return (TCL_ERROR);
  1193. X    }
  1194. X
  1195. X    Tcl_Return(interp, getenv(argv[1]), TCL_STATIC);
  1196. X
  1197. X    return TCL_OK;
  1198. X}
  1199. X
  1200. Xint
  1201. XcmdEcho(clientData, interp, argc, argv)
  1202. X    char *clientData;
  1203. X    Tcl_Interp *interp;
  1204. X    int argc;
  1205. X    char **argv;
  1206. X{
  1207. X    int i = 1, newline = 1;
  1208. X
  1209. X    if (argc > 1 && (strcmp(argv[1], "-n") == 0)) {
  1210. X    newline = 0;
  1211. X    i++;
  1212. X    }
  1213. X
  1214. X    for (; i < argc ; i++) 
  1215. X    fprintf(FingerOutput, "%s ", argv[i]);
  1216. X    
  1217. X    if (newline)
  1218. X    fprintf(FingerOutput, "\n");
  1219. X
  1220. X    return TCL_OK;
  1221. X}
  1222. X
  1223. Xint
  1224. XcmdFill(clientData, interp, argc, argv)
  1225. X    char *clientData;
  1226. X    Tcl_Interp *interp;
  1227. X    int argc;
  1228. X    char **argv;
  1229. X{
  1230. X    int i, linelen, indent, j, length, hanging = 0;
  1231. X
  1232. X    if (argc < 4) {
  1233. X    sprintf(interp->result,
  1234. X        "incorrect number of parameters to \"%s\" command", argv[0]);
  1235. X    return (TCL_ERROR);
  1236. X    }
  1237. X    
  1238. X    indent = atoi(argv[1]);
  1239. X    linelen = atoi(argv[2]);
  1240. X
  1241. X    i = 3;
  1242. X    if (argc > 4 && (strcmp(argv[3], "-h") == 0)) {
  1243. X    hanging = 1;
  1244. X    i++;
  1245. X    }
  1246. X
  1247. X    for (; i < argc ; ) {
  1248. X    /* if there's a hanging indent, don't pad first time */
  1249. X    if (i != 4 || !hanging)
  1250. X        for (j = 1; j <= indent; j++)
  1251. X        fputc(' ', FingerOutput);
  1252. X
  1253. X    /* length is the length after the second word */
  1254. X    length = strlen(argv[i]);
  1255. X    fprintf(FingerOutput, "%s", argv[i++]);
  1256. X    if (i == argc)
  1257. X        break;
  1258. X
  1259. X    length += strlen(argv[i]) + indent + 1;
  1260. X
  1261. X    /* while the _next_ word fits, do this ... */
  1262. X    while (length <= linelen && i < argc) {
  1263. X        /* print the next word, add length for following word */
  1264. X        fprintf(FingerOutput, " %s", argv[i]);
  1265. X        if (++i == argc)
  1266. X        break;
  1267. X        length += strlen(argv[i]) + 1;
  1268. X    }
  1269. X
  1270. X    if (i == argc)
  1271. X        break;
  1272. X
  1273. X    fputc('\n', FingerOutput);
  1274. X    }
  1275. X
  1276. X    return TCL_OK;
  1277. X}
  1278. X
  1279. X/* Local Variables: */
  1280. X/* compile-command: "make" */
  1281. X/* End: */
  1282. //go.sysin dd *
  1283. echo x - tcl_cmds.h
  1284. sed 's/^X//' > tcl_cmds.h << '//go.sysin dd *'
  1285. Xextern int cmdToUpper( char *, Tcl_Interp *, int, char ** );
  1286. Xextern int cmdToLower( char *, Tcl_Interp *, int, char ** );
  1287. Xextern int cmdGetEnv( char *, Tcl_Interp *, int, char ** );
  1288. Xextern int cmdEcho( char *, Tcl_Interp *, int, char ** );
  1289. Xextern int cmdFill( char *, Tcl_Interp *, int, char ** );
  1290. //go.sysin dd *
  1291. echo x - uniqueproc.c
  1292. sed 's/^X//' > uniqueproc.c << '//go.sysin dd *'
  1293. X/*
  1294. X * This file contains a function "unique_proc" which ensures that the
  1295. X * process calling it is the only instance of this program running
  1296. X * under this userid.
  1297. X *
  1298. X * It returns TRUE if this process is the only one, and FALSE otherwise.
  1299. X *
  1300. X * It uses a PID file and the "ps" program to do its job.  It is not
  1301. X * bulletproof.
  1302. X */
  1303. X
  1304. X#include <stdlib.h>
  1305. X#include <stdio.h>
  1306. X#include <errno.h>
  1307. X#include <string.h>
  1308. X#include "plan_proto.h"
  1309. X
  1310. Xvoid writepid(const char*, int);
  1311. X
  1312. X#define BUFFER_SIZE (512)
  1313. X#define FALSE (0)
  1314. X#define TRUE (!FALSE)
  1315. X
  1316. Xint unique_proc() {
  1317. X    int mypid, procpid, n;
  1318. X    FILE *ps_out, *pid_file;
  1319. X    static char buf[BUFFER_SIZE], cmd_name[BUFFER_SIZE];
  1320. X    static char pid_cmd_name[BUFFER_SIZE], pid_file_name[BUFFER_SIZE];
  1321. X    char *home;
  1322. X
  1323. X    /* create the command to get the ps output for this process */
  1324. X    mypid = getpid();
  1325. X    sprintf( buf, "ps -c%d", mypid );
  1326. X
  1327. X    /* open the command for read */
  1328. X    if( (ps_out = popen( buf, "r" )) == NULL ) {
  1329. X    fprintf( stderr, "unique_proc: popen #1 failed. errno=%d\n", errno );
  1330. X    return FALSE;
  1331. X    } /* if */
  1332. X
  1333. X    /* suck up the header line */
  1334. X    fgets( buf, BUFFER_SIZE, ps_out );
  1335. X
  1336. X    /* scan the command name */
  1337. X    n = fscanf( ps_out, "%*20c%s", cmd_name );
  1338. X    if( pclose(ps_out) == -1 ) {
  1339. X    fprintf( stderr, "unique_proc: pclose #1 failed, errno =%d\n", errno );
  1340. X    return FALSE;
  1341. X    } /* if */
  1342. X
  1343. X    if( n != 1 ) {
  1344. X    fprintf( stderr, "unique_proc: cannot scan command name (n=%d)\n", n );
  1345. X    return FALSE;
  1346. X    } /* if */
  1347. X
  1348. X    /* get the home directory for this user */
  1349. X    if( (home = getenv("HOME")) == NULL ) {
  1350. X    fprintf( stderr, "unique_proc: getenv(\"HOME\") failed\n" );
  1351. X    return FALSE;
  1352. X    } /* if */
  1353. X
  1354. X    /* create the pidfile name for this command */
  1355. X    sprintf( pid_file_name, "%s/.%spid", home, cmd_name );
  1356. X
  1357. X    /* open the pid file */
  1358. X    if( (pid_file = fopen( pid_file_name, "r" )) == NULL ) {
  1359. X    /* assume this is the only instance, and write pid to file */
  1360. X    writepid( pid_file_name, mypid );
  1361. X    return TRUE;
  1362. X    } /* if */
  1363. X
  1364. X    /* read the pid */
  1365. X    if( (n = fscanf( pid_file, "%d", &procpid )) != 1 ) {
  1366. X    /* can't scan pid, assume this is only process, write pid to file */
  1367. X    fclose( pid_file );
  1368. X    writepid( pid_file_name, mypid );
  1369. X    return TRUE;
  1370. X    } /* if */
  1371. X
  1372. X    /* check out the pid read */
  1373. X    sprintf( buf, "ps -c%d", procpid );
  1374. X
  1375. X    /* open the command for read */
  1376. X    if( (ps_out = popen( buf, "r" )) == NULL ) {
  1377. X    fprintf( stderr, "unique_proc: popen #2 failed. errno=%d\n", errno );
  1378. X    return FALSE;
  1379. X    } /* if */
  1380. X
  1381. X    /* suck up the header line */
  1382. X    fgets( buf, BUFFER_SIZE, ps_out );
  1383. X
  1384. X    /* scan the command name */
  1385. X    n = fscanf( ps_out, "%*20c%s", pid_cmd_name );
  1386. X    if( pclose(ps_out) == -1 ) {
  1387. X    fprintf( stderr, "unique_proc: pclose #2 failed, errno =%d\n", errno );
  1388. X    return FALSE;
  1389. X    } /* if */
  1390. X
  1391. X    if( n == EOF ) {
  1392. X    /*
  1393. X     * no such process...
  1394. X     * therefore old process is dead
  1395. X     * write new pid
  1396. X     */
  1397. X    writepid( pid_file_name, mypid );
  1398. X    return TRUE;
  1399. X    } else if( strcmp( cmd_name, pid_cmd_name ) != 0 ) {
  1400. X    /*
  1401. X     * names do not match, so pid match is a coincidence.
  1402. X     * write new pid
  1403. X     */
  1404. X    writepid( pid_file_name, mypid );
  1405. X    return TRUE;
  1406. X    } else {
  1407. X    /*
  1408. X     * found an old instance of this program
  1409. X     */
  1410. X    return FALSE;
  1411. X    } /* if */
  1412. X} /* unique_proc */
  1413. X
  1414. Xvoid writepid( const char* pidfilename, int pid ) {
  1415. X    FILE *pid_file;
  1416. X
  1417. X    if( (pid_file = fopen( pidfilename, "w" )) == NULL ) {
  1418. X    fprintf( stderr, "unique_proc: cannot open pid file ('%s') for write\n", pidfilename );
  1419. X    } else {
  1420. X    fprintf( pid_file, "%d\n", pid );
  1421. X    fclose( pid_file );
  1422. X    } /* if */
  1423. X} /* writepid */
  1424. //go.sysin dd *
  1425. exit
  1426.  
  1427. --
  1428. Blair MacIntyre --- bm@cs.columbia.edu --- CS Department, Columbia University
  1429.