home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume7 / vms_tools / part02 < prev    next >
Text File  |  1986-12-02  |  47KB  |  1,842 lines

  1. Subject:  v07i070:  Unix-like tools for VMS systems, Part02/02
  2. Newsgroups: mod.sources
  3. Approved: mirror!rs
  4.  
  5. Submitted by: David Albrecht <calmasd!dca@edison.GE.COM>
  6. Mod.sources: Volume 7, Issue 70
  7. Archive-name: vms_tools/Part02
  8.  
  9. #!/bin/sh
  10. #
  11. # The following files are VMS C programs which will
  12. # interface with VMS to provide some unix-like capabilities.
  13. # One file, (unixlike) gives a simplistic introduction to the
  14. # tools in 'roff format.
  15. # Most of the files are well documented in comments at the
  16. # beginning as to their function and are totally stand alone.
  17. # The file cd.com is a DCL script for the CD command under
  18. # VMS for those people who don't possess the C compiler.
  19. # The file reset is a one line symbol definition which should
  20. # be added to a symbol definition file (ala. login.com) to add
  21. # a command (RESET) which will reset the JOB tracking system when
  22. # it goes astray.
  23. # David Albrecht
  24. #
  25. echo 'Start of vms, part 01 of 02:'
  26. echo 'x - jobs.c'
  27. sed 's/^X//' > jobs.c << '/'
  28. X/*
  29. X    jobs - David C. Albrecht 4/4/86
  30. X
  31. X    jobs provides a facility for printing the status of jobs issued
  32. X    into the background by the E(x)tend utility.  It will also
  33. X    update the status of finished jobs and print completion for
  34. X    when the mode is not set to immediate notify.
  35. X*/
  36. X
  37. X#define MAXSTRING 256
  38. X#define NULL 0
  39. X
  40. X/*
  41. X    vms interface files
  42. X*/
  43. X#include <rms.h>
  44. X#include <stdio.h>
  45. X#include <stsdef.h>
  46. X#include <ssdef.h>
  47. X#include <descrip.h>
  48. X
  49. X/* descriptors for passing to vms commands */
  50. Xstruct dsc$descriptor_s desc,desc1;
  51. X
  52. Xmain (argc,argv)
  53. X    int argc;
  54. X    char **argv;
  55. X
  56. X{   int i;
  57. X    int next_job;    /* subprocess number of job */
  58. X    int job_status;    /* flag for existence of job_status symbol */
  59. X    char str[MAXSTRING];/* multi-purpose:
  60. X                get job_status symbol value
  61. X                get job logical value
  62. X            */
  63. X    char jobstr[15];    /* current bg jobs indicator string */
  64. X    char joblogical[15];/* name of individual job logical */
  65. X
  66. X    /*
  67. X    get background job process string
  68. X    */
  69. X    if (job_status = getsymbol("JOB_STATUS",str)) {
  70. X    /*
  71. X        update any completed background jobs
  72. X    */
  73. X    strcpy(jobstr,str);
  74. X    update_job_status(jobstr,&next_job);
  75. X    }
  76. X    else {
  77. X    *jobstr = '\0';
  78. X    next_job = 1;
  79. X    }
  80. X    /*
  81. X    there is an 'X' in the job status string for all background
  82. X    jobs currently still outstanding
  83. X    */
  84. X    for (i = 0; i < strlen(jobstr); i++) {
  85. X    if (jobstr[i] != ' ') {
  86. X        /*
  87. X        each job maintains a JOB_'number' logical which
  88. X        indicates current execute status of the job
  89. X        */
  90. X        sprintf(joblogical,"JOB_%d",i+1);
  91. X        getlogical(joblogical,str);
  92. X        /*
  93. X        print the job execute status
  94. X        */
  95. X        printf("[%d] %s\n",i+1,str);
  96. X    }
  97. X    }
  98. X    /*
  99. X    set or delete updated job process string
  100. X    */
  101. X    if (*jobstr) {
  102. X    setsymbol("JOB_STATUS",jobstr);
  103. X    }
  104. X    else if (job_status) {
  105. X    delsymbol("JOB_STATUS");
  106. X    }
  107. X}
  108. X/*
  109. X    dellogical
  110. X    delete a logical from the job space
  111. X*/
  112. Xdellogical(logical_name)
  113. Xchar *logical_name;
  114. X{
  115. X
  116. X    setdesc(&desc,logical_name,strlen(logical_name));
  117. X    lib$delete_logical(&desc,0);
  118. X}
  119. X/*
  120. X    delsymbol
  121. X    delete a symbol from the global table
  122. X*/
  123. Xdelsymbol(symbol_name)
  124. Xchar *symbol_name;
  125. X{   int tbl;
  126. X
  127. X    setdesc(&desc,symbol_name,strlen(symbol_name));
  128. X    tbl = 2;
  129. X    lib$delete_symbol(&desc,&tbl);
  130. X}
  131. X/*
  132. X    getlogical
  133. X    get a logical's translation
  134. X*/
  135. Xgetlogical(logical_name,value)
  136. Xchar *logical_name,*value;
  137. X{   int valuelen;
  138. X
  139. X    setdesc(&desc,logical_name,strlen(logical_name));
  140. X    setdesc(&desc1,value,MAXSTRING-1);
  141. X    valuelen = 0;
  142. X    lib$sys_trnlog(&desc,&valuelen,&desc1,0,0,0);
  143. X    value[valuelen] = '\0';
  144. X    if (!strcmp(logical_name,value)) {
  145. X    return(0);
  146. X    }
  147. X    return(1);
  148. X
  149. X}
  150. X
  151. X/*
  152. X    getsymbol
  153. X    get a symbol's value
  154. X*/
  155. Xgetsymbol(symbol_name,value)
  156. Xchar *symbol_name,*value;
  157. X{   int valuelen,status;
  158. X
  159. X    setdesc(&desc,symbol_name,strlen(symbol_name));
  160. X    setdesc(&desc1,value,MAXSTRING-1);
  161. X    valuelen = 0;
  162. X    status = lib$get_symbol(&desc,&desc1,&valuelen,0);
  163. X    if (status & STS$M_SUCCESS) {
  164. X    value[valuelen] = '\0';
  165. X    return(1);
  166. X    }
  167. X    else {
  168. X    return(0);
  169. X    }
  170. X}
  171. X
  172. X/*
  173. X    setdesc
  174. X    set a descriptor entry for a standard ascii string
  175. X*/
  176. Xsetdesc(descr,str,strlen)
  177. Xstruct dsc$descriptor_s *descr;
  178. Xchar *str;
  179. Xint strlen;
  180. X{
  181. X    descr->dsc$w_length = strlen;
  182. X    descr->dsc$a_pointer = str;
  183. X    descr->dsc$b_class = DSC$K_CLASS_S;    /* String desc class */
  184. X    descr->dsc$b_dtype = DSC$K_DTYPE_T;    /* Ascii string type */
  185. X}
  186. X
  187. X/*
  188. X    setsymbol
  189. X    set a symbol in the global space
  190. X*/
  191. Xsetsymbol(symbol_name,value)
  192. Xchar *symbol_name,*value;
  193. X{   int tbl;
  194. X
  195. X    setdesc(&desc,symbol_name,strlen(symbol_name));
  196. X    setdesc(&desc1,value,strlen(value));
  197. X    tbl = 2;
  198. X    lib$set_symbol(&desc,&desc1,&tbl);
  199. X}
  200. X/*
  201. X    update_job_status
  202. X    update the status of any completed background jobs.
  203. X    print completion code to the screen for any non-immediate
  204. X    notification style jobs.
  205. X*/
  206. Xupdate_job_status(active_jobs,next_job)
  207. Xchar *active_jobs;
  208. Xint *next_job;
  209. X{   int i;
  210. X    char joblogical[MAXSTRING],status_info[MAXSTRING],*active;
  211. X
  212. X    /*
  213. X    there is an 'X' in the active_jobs string for all background
  214. X    jobs currently outstanding.
  215. X    */
  216. X    for (i = 0; i < strlen(active_jobs); i++) {
  217. X    if (active_jobs[i] != ' ') {
  218. X        /*
  219. X        each job maintains a JOB_'number' logical which
  220. X        indicates current execute status of the job
  221. X        */
  222. X        sprintf(joblogical,"JOB_%d",i+1);
  223. X        getlogical(joblogical,status_info);
  224. X        /*
  225. X        a symbol prefaced with DONE means the job is complete
  226. X        */
  227. X        if (!strncmp(status_info,"DONE",4)) {
  228. X        if (strcmp(status_info,"DONE")) {
  229. X            /*
  230. X            if DONE is followed by additional info then
  231. X            the job was not immediately notified so its
  232. X            completion is signaled now
  233. X            */
  234. X            printf("[%d] %s\n",i+1,status_info);
  235. X        }
  236. X        /*
  237. X            remove the completed job's logical
  238. X        */
  239. X        dellogical(joblogical);
  240. X        /*
  241. X            remove the active indicator from the active_jobs
  242. X        */
  243. X        active_jobs[i] = ' ';
  244. X        }
  245. X    }
  246. X    }
  247. X    /*
  248. X    update active_jobs string to remove trailing blanks
  249. X    */
  250. X    active = active_jobs + strlen(active_jobs) - 1;
  251. X    while (active >= active_jobs && *active == ' ') *(active--) = '\0';
  252. X    /*
  253. X    set number of next_job sub process
  254. X    */
  255. X    *next_job = strlen(active_jobs)+1;
  256. X}
  257. X/*
  258. X    upshift
  259. X    uppercase a character string
  260. X*/
  261. Xupshift(upname,lwname)
  262. Xchar *upname,*lwname;
  263. X{
  264. X    while (*(upname++) = toupper(*(lwname++)));
  265. X}
  266. X
  267. /
  268. echo 'x - kill.c'
  269. sed 's/^X//' > kill.c << '/'
  270. X/*
  271. X    kill - David C. Albrecht 8/20/86
  272. X
  273. X    kill provides a facility for aborting jobs issued
  274. X    into the background by the E(x)tend utility.  It will also
  275. X    update the status of finished jobs and print completion for
  276. X    when the mode is not set to immediate notify.
  277. X*/
  278. X
  279. X#define MAXSTRING 256
  280. X#define NULL 0
  281. X
  282. X/*
  283. X    vms interface files
  284. X*/
  285. X#include <rms.h>
  286. X#include <stdio.h>
  287. X#include <stsdef.h>
  288. X#include <ssdef.h>
  289. X#include <descrip.h>
  290. X
  291. X/* descriptors for passing to vms commands */
  292. Xstruct dsc$descriptor_s desc,desc1;
  293. X
  294. Xmain (argc,argv)
  295. X    int argc;
  296. X    char **argv;
  297. X
  298. X{   int i;
  299. X    int next_job;    /* subprocess number of job */
  300. X    int job_status;    /* flag for existence of job_status symbol */
  301. X    char str[MAXSTRING];/* multi-purpose:
  302. X                get job_status symbol value
  303. X                get job logical value
  304. X            */
  305. X    char jobstr[15];    /* current bg jobs indicator string */
  306. X    char joblogical[15];/* name of individual job logical */
  307. X    long pid1, pid2;    /* process id variables */
  308. X
  309. X    /*
  310. X    get background job process string
  311. X    */
  312. X    if (job_status = getsymbol("JOB_STATUS",str)) {
  313. X    /*
  314. X        update any completed background jobs
  315. X    */
  316. X    strcpy(jobstr,str);
  317. X    update_job_status(jobstr,&next_job);
  318. X    }
  319. X    else {
  320. X    *jobstr = '\0';
  321. X    next_job = 1;
  322. X    }
  323. X    /*
  324. X    there is an 'X' in the job status string for all background
  325. X    jobs currently still outstanding
  326. X    */
  327. X    if (!argv[1]) {
  328. X    fprintf(stderr, "kill {pid | %jobno}\n");
  329. X    exit(1);
  330. X    }
  331. X    if (*argv[1] == '%') {
  332. X    if (sscanf(argv[1] + 1, "%d", &i) < 1) {
  333. X        fprintf(stderr, "kill {pid | %jobno}\n");
  334. X        exit(1);
  335. X    }
  336. X    if (i <= strlen(jobstr) && jobstr[i - 1] != ' ') {
  337. X        /*
  338. X        each job maintains a JOB_'number' logical which
  339. X        indicates current execute status of the job
  340. X        */
  341. X        sprintf(joblogical,"JOB_%d",i);
  342. X        getlogical(joblogical,str);
  343. X        /*
  344. X        kill the job and update the job status string
  345. X        */
  346. X        kill_pid(str);
  347. X        jobstr[i - 1] = ' ';
  348. X        printf("[%d] %s killed\n", i, str);
  349. X    }
  350. X    else {
  351. X        printf("%s not found\n", argv[1]);
  352. X    }
  353. X    }
  354. X    else {
  355. X    if (sscanf(argv[1], "%lx", &pid1) < 1) {
  356. X        fprintf(stderr, "invalid pid %s\n", argv[1]);
  357. X        exit(1);
  358. X    }
  359. X    for (i = 0; i < strlen(jobstr); i++) {
  360. X        if (jobstr[i] != ' ') {
  361. X        /*
  362. X            each job maintains a JOB_'number' logical which
  363. X            indicates current execute status of the job
  364. X        */
  365. X        sprintf(joblogical,"JOB_%d",i+1);
  366. X        getlogical(joblogical,str);
  367. X        if (sscanf(str, "%lx", &pid2) >= 1) {
  368. X            if (pid1 == pid2) {
  369. X            jobstr[i] = ' ';
  370. X            break;
  371. X            }
  372. X        }
  373. X        }
  374. X    }
  375. X    kill_pid(argv[1]);
  376. X    if (i < strlen(jobstr)) {
  377. X        printf("[%d] %s killed\n", i + 1, str);
  378. X    }
  379. X    else {
  380. X        printf("%s killed\n", argv[1]);
  381. X    }
  382. X    }
  383. X    /*
  384. X    set or delete updated job process string
  385. X    */
  386. X    if (*jobstr) {
  387. X    setsymbol("JOB_STATUS",jobstr);
  388. X    }
  389. X    else if (job_status) {
  390. X    delsymbol("JOB_STATUS");
  391. X    }
  392. X}
  393. X/*
  394. X    dellogical
  395. X    delete a logical from the job space
  396. X*/
  397. Xdellogical(logical_name)
  398. Xchar *logical_name;
  399. X{
  400. X
  401. X    setdesc(&desc,logical_name,strlen(logical_name));
  402. X    lib$delete_logical(&desc,0);
  403. X}
  404. X/*
  405. X    delsymbol
  406. X    delete a symbol from the global table
  407. X*/
  408. Xdelsymbol(symbol_name)
  409. Xchar *symbol_name;
  410. X{   int tbl;
  411. X
  412. X    setdesc(&desc,symbol_name,strlen(symbol_name));
  413. X    tbl = 2;
  414. X    lib$delete_symbol(&desc,&tbl);
  415. X}
  416. X/*
  417. X    getlogical
  418. X    get a logical's translation
  419. X*/
  420. Xgetlogical(logical_name,value)
  421. Xchar *logical_name,*value;
  422. X{   int valuelen;
  423. X
  424. X    setdesc(&desc,logical_name,strlen(logical_name));
  425. X    setdesc(&desc1,value,MAXSTRING-1);
  426. X    valuelen = 0;
  427. X    lib$sys_trnlog(&desc,&valuelen,&desc1,0,0,0);
  428. X    value[valuelen] = '\0';
  429. X    if (!strcmp(logical_name,value)) {
  430. X    return(0);
  431. X    }
  432. X    return(1);
  433. X
  434. X}
  435. X
  436. X/*
  437. X    getsymbol
  438. X    get a symbol's value
  439. X*/
  440. Xgetsymbol(symbol_name,value)
  441. Xchar *symbol_name,*value;
  442. X{   int valuelen,status;
  443. X
  444. X    setdesc(&desc,symbol_name,strlen(symbol_name));
  445. X    setdesc(&desc1,value,MAXSTRING-1);
  446. X    valuelen = 0;
  447. X    status = lib$get_symbol(&desc,&desc1,&valuelen,0);
  448. X    if (status & STS$M_SUCCESS) {
  449. X    value[valuelen] = '\0';
  450. X    return(1);
  451. X    }
  452. X    else {
  453. X    return(0);
  454. X    }
  455. X}
  456. X
  457. X/*
  458. X    killpid
  459. X    kill a process id specified by a hex number string
  460. X*/
  461. Xkill_pid(str)
  462. Xchar *str;
  463. X{   long pid;
  464. X    int i;
  465. X
  466. X    if (sscanf(str, "%lx", &pid) < 1) {
  467. X    fprintf(stderr, "invalid process id %s\n", str);
  468. X    exit(1);
  469. X    }
  470. X    i = SYS$DELPRC(&pid,0);
  471. X    switch (i) {
  472. X
  473. X    case SS$_NORMAL:
  474. X        break;
  475. X
  476. X    case SS$_ACCVIO:
  477. X        fprintf(stderr, "access violation, process not killed\n");
  478. X        exit(1);
  479. X
  480. X    case SS$_NONEXPR:
  481. X        fprintf(stderr, "invalid process specification, process not killed\n");
  482. X        exit(1);
  483. X
  484. X    case SS$_NOPRIV:
  485. X        fprintf(stderr, "no privledge for attempted operation, process not killed\n");
  486. X        exit(1);
  487. X
  488. X    default:
  489. X        fprintf(stderr, "error killing process, return = %d, process not killed",i);
  490. X        exit(1);
  491. X
  492. X    }
  493. X}
  494. X/*
  495. X    setdesc
  496. X    set a descriptor entry for a standard ascii string
  497. X*/
  498. Xsetdesc(descr,str,strlen)
  499. Xstruct dsc$descriptor_s *descr;
  500. Xchar *str;
  501. Xint strlen;
  502. X{
  503. X    descr->dsc$w_length = strlen;
  504. X    descr->dsc$a_pointer = str;
  505. X    descr->dsc$b_class = DSC$K_CLASS_S;    /* String desc class */
  506. X    descr->dsc$b_dtype = DSC$K_DTYPE_T;    /* Ascii string type */
  507. X}
  508. X
  509. X/*
  510. X    setsymbol
  511. X    set a symbol in the global space
  512. X*/
  513. Xsetsymbol(symbol_name,value)
  514. Xchar *symbol_name,*value;
  515. X{   int tbl;
  516. X
  517. X    setdesc(&desc,symbol_name,strlen(symbol_name));
  518. X    setdesc(&desc1,value,strlen(value));
  519. X    tbl = 2;
  520. X    lib$set_symbol(&desc,&desc1,&tbl);
  521. X}
  522. X/*
  523. X    update_job_status
  524. X    update the status of any completed background jobs.
  525. X    print completion code to the screen for any non-immediate
  526. X    notification style jobs.
  527. X*/
  528. Xupdate_job_status(active_jobs,next_job)
  529. Xchar *active_jobs;
  530. Xint *next_job;
  531. X{   int i;
  532. X    char joblogical[MAXSTRING],status_info[MAXSTRING],*active;
  533. X
  534. X    /*
  535. X    there is an 'X' in the active_jobs string for all background
  536. X    jobs currently outstanding.
  537. X    */
  538. X    for (i = 0; i < strlen(active_jobs); i++) {
  539. X    if (active_jobs[i] != ' ') {
  540. X        /*
  541. X        each job maintains a JOB_'number' logical which
  542. X        indicates current execute status of the job
  543. X        */
  544. X        sprintf(joblogical,"JOB_%d",i+1);
  545. X        getlogical(joblogical,status_info);
  546. X        /*
  547. X        a symbol prefaced with DONE means the job is complete
  548. X        */
  549. X        if (!strncmp(status_info,"DONE",4)) {
  550. X        if (strcmp(status_info,"DONE")) {
  551. X            /*
  552. X            if DONE is followed by additional info then
  553. X            the job was not immediately notified so its
  554. X            completion is signaled now
  555. X            */
  556. X            printf("[%d] %s\n",i+1,status_info);
  557. X        }
  558. X        /*
  559. X            remove the completed job's logical
  560. X        */
  561. X        dellogical(joblogical);
  562. X        /*
  563. X            remove the active indicator from the active_jobs
  564. X        */
  565. X        active_jobs[i] = ' ';
  566. X        }
  567. X    }
  568. X    }
  569. X    /*
  570. X    update active_jobs string to remove trailing blanks
  571. X    */
  572. X    active = active_jobs + strlen(active_jobs) - 1;
  573. X    while (active >= active_jobs && *active == ' ') *(active--) = '\0';
  574. X    /*
  575. X    set number of next_job sub process
  576. X    */
  577. X    *next_job = strlen(active_jobs)+1;
  578. X}
  579. X/*
  580. X    upshift
  581. X    uppercase a character string
  582. X*/
  583. Xupshift(upname,lwname)
  584. Xchar *upname,*lwname;
  585. X{
  586. X    while (*(upname++) = toupper(*(lwname++)));
  587. X}
  588. X
  589. /
  590. echo 'x - pushd.c'
  591. sed 's/^X//' > pushd.c << '/'
  592. X#define NULL 0
  593. X#define MAXSTRING 132
  594. X
  595. X#include <rms.h>
  596. X#include <stdio.h>
  597. X#include <stsdef.h>
  598. X#include <ssdef.h>
  599. X#include <descrip.h>
  600. X
  601. Xstruct dsc$descriptor_s desc,desc1;
  602. X
  603. Xmain (argc,argv)
  604. X    int argc;
  605. X    char **argv;
  606. X
  607. X{   char curdir[MAXSTRING],testdir[MAXSTRING],todir[MAXSTRING];
  608. X    char *p1,*dirspec,*strpos();
  609. X    int i;
  610. X
  611. X    if (argc <= 1) {
  612. X    getlogical("SYS$LOGIN",todir);
  613. X    if (save_and_chdir(todir,1)) {
  614. X        invalid_dirspec(todir);
  615. X        return(1);
  616. X    }
  617. X    }
  618. X    else {
  619. X    p1 = argv[1];
  620. X    if (strchr(p1,'[')
  621. X     || strpos(p1,"..")
  622. X     || strchr(p1,'/')) {
  623. X        if (save_and_chdir(p1,1)) {
  624. X        invalid_dirspec(p1);
  625. X        return(1);
  626. X        }
  627. X    }
  628. X    else if (getlogical(p1,todir)) {
  629. X        if (save_and_chdir(p1,1)) {
  630. X        invalid_dirspec(p1);
  631. X        return(1);
  632. X        }
  633. X    }
  634. X    else {
  635. X        dirspec = strrchr(p1,':');
  636. X        if (!dirspec) {
  637. X        *todir = '\0';
  638. X        dirspec = p1;
  639. X        }
  640. X        else {
  641. X        i = dirspec-p1+1;
  642. X        strncpy(todir,p1,i);
  643. X        todir[i]='\0';
  644. X        dirspec++;
  645. X        }
  646. X        strcat(todir,"[");
  647. X        if (*dirspec != '.'
  648. X         && *dirspec != '-'
  649. X         && dirspec == p1) strcat(todir,".");
  650. X        strcat(todir,dirspec);
  651. X        strcat(todir,"]");
  652. X        if (save_and_chdir(todir,1)) {
  653. X        invalid_dirspec(todir);
  654. X        return(1);
  655. X        }
  656. X    }
  657. X    }
  658. X}
  659. Xgetlogical(logical_name,value)
  660. Xchar *logical_name,*value;
  661. X{   int valuelen;
  662. X    char cap_name[MAXSTRING];
  663. X
  664. X    upshift(cap_name,logical_name);
  665. X    setdesc(&desc,cap_name,strlen(cap_name));
  666. X    setdesc(&desc1,value,MAXSTRING-1);
  667. X    valuelen = 0;
  668. X    lib$sys_trnlog(&desc,&valuelen,&desc1,0,0,0);
  669. X    value[valuelen] = '\0';
  670. X    if (!strcmp(cap_name,value)) {
  671. X    return(0);
  672. X    }
  673. X    return(1);
  674. X
  675. X}
  676. Xgetsymbol(symbol_name,value)
  677. Xchar *symbol_name,*value;
  678. X{   int valuelen,status;
  679. X
  680. X    setdesc(&desc,symbol_name,strlen(symbol_name));
  681. X    setdesc(&desc1,value,MAXSTRING-1);
  682. X    valuelen = 0;
  683. X    status = lib$get_symbol(&desc,&desc1,&valuelen,0);
  684. X    if (status & STS$M_SUCCESS) {
  685. X    value[valuelen] = '\0';
  686. X    return(1);
  687. X    }
  688. X    else {
  689. X    return(0);
  690. X    }
  691. X}
  692. Xgetwd(pwd)
  693. Xchar *pwd;
  694. X{   int pwdlen;
  695. X
  696. X    setdesc(&desc,pwd,MAXSTRING-1);
  697. X    pwdlen = 0;
  698. X    sys$setddir(0,&pwdlen,&desc);
  699. X    pwd[pwdlen] = '\0';
  700. X}
  701. Xinvalid_dirspec(dirspec)
  702. Xchar *dirspec;
  703. X{
  704. X    fprintf(stderr,"invalid directory %s\n",dirspec);
  705. X}
  706. Xsetdesc(descr,str,strlen)
  707. Xstruct dsc$descriptor_s *descr;
  708. Xchar *str;
  709. Xint strlen;
  710. X{
  711. X    descr->dsc$w_length = strlen;
  712. X    descr->dsc$a_pointer = str;
  713. X    descr->dsc$b_class = DSC$K_CLASS_S;    /* String desc class */
  714. X    descr->dsc$b_dtype = DSC$K_DTYPE_T;    /* Ascii string type */
  715. X}
  716. Xsetlogical(logical_name,value)
  717. Xchar *logical_name,*value;
  718. X{   char cap_name[MAXSTRING];
  719. X
  720. X    upshift(cap_name,logical_name);
  721. X    setdesc(&desc,cap_name,strlen(cap_name));
  722. X    setdesc(&desc1,value,strlen(value));
  723. X    lib$set_logical(&desc,&desc1,0);
  724. X}
  725. Xsetsymbol(symbol_name,value)
  726. Xchar *symbol_name,*value;
  727. X{   int tbl;
  728. X
  729. X    setdesc(&desc,symbol_name,strlen(symbol_name));
  730. X    setdesc(&desc1,value,strlen(value));
  731. X    tbl = 2;
  732. X    lib$set_symbol(&desc,&desc1,&tbl);
  733. X}
  734. Xupshift(upname,lwname)
  735. Xchar *upname,*lwname;
  736. X{
  737. X    while (*(upname++) = toupper(*(lwname++)));
  738. X}
  739. Xsave_and_chdir(todir,permanent_flag)
  740. Xchar *todir;
  741. Xint permanent_flag;
  742. X{   char pushstack[MAXSTRING],oldstack[MAXSTRING];
  743. X    int i;
  744. X
  745. X    getlogical("SYS$DISK",pushstack);
  746. X    getwd(pushstack+strlen(pushstack));
  747. X    if (i = chdir(todir,permanent_flag)) {
  748. X    return(i);
  749. X    }
  750. X    printf("%s\n",pushstack);
  751. X    if (getlogical("dir_stack",oldstack)) {
  752. X    strcat(pushstack,",");
  753. X    strcat(pushstack,oldstack);
  754. X    }
  755. X    setlogical("dir_stack",pushstack);
  756. X    return(i);
  757. X}
  758. Xchar *strpos(str,searchstr)
  759. Xchar *str,*searchstr;
  760. X{   char *matchc,c,*matchstr;
  761. X
  762. X    matchc = searchstr;
  763. X    c = *matchc++;
  764. X    while (c) {
  765. X    if (!*str) {
  766. X        return(NULL);
  767. X    }
  768. X    else {
  769. X        matchstr = str++;
  770. X        while (c == *matchstr && *matchstr++) {
  771. X        c = *matchc++;
  772. X        }
  773. X        if (!c) {
  774. X         return(str - 1);
  775. X        }
  776. X        matchc = searchstr;
  777. X        c = *matchc++;
  778. X    }
  779. X    }
  780. X    return(NULL);
  781. X}
  782. /
  783. echo 'x - reset'
  784. sed 's/^X//' > reset << '/'
  785. XRESET :== "DELETE/SYM/GLOB JOB_STATUS"
  786. /
  787. echo 'x - unixlike'
  788. sed 's/^X//' > unixlike << '/'
  789. X.so macs.txt
  790. X
  791. X.sp 10
  792. X.(l C
  793. XUNIX-LIKE TOOLS
  794. XAVAILABLE FROM VMS
  795. X.sp 2
  796. X\*(td
  797. X.)l
  798. X.fi
  799. X.bp
  800. X.sh  1 "CD"
  801. X.pp
  802. XThe Unix change directory command's most attractive feature is that
  803. Xit checks target directorys.  This CD command will not only check
  804. Xtarget directories but also allow some limited specification of
  805. Xpaths in Unix format as well as VMS format.  In vms format the
  806. Xbrackets around the directory specification can by omitted.  It
  807. Xwill even change to a VMS logical.
  808. X.sh 2 "PUSHD/POPD"
  809. X.pp
  810. XPUSHD just as in Unix is an extension to CD which saves the current
  811. Xdirectory on a directory stack.  POPD is the symmetrical tool
  812. Xwhich provides the facility to return to the \&'pushed' directory.
  813. X.sh 3 "CDPATH's"
  814. X.pp
  815. XA CDPATH is a path of directories under UNIX which the user can
  816. Xspecify.  The CD command will search these directories for your
  817. Xtarget directory.  A facility which approximates these capabilities
  818. Xhas been provided under VMS which actually gives some additional
  819. Xcapabilities.  Define a symbol called \&'CDPATH' in the DCL
  820. Xshell which is a list of blank separated VMS directories.
  821. XInvoking the CDHASH program will search these directories and
  822. Xdefine a logical for each subdirectory the value of which is
  823. Xthe full path location.  CD'ing to a directory from anywhere
  824. Xin the system thus becomes a matter of typing the subdirectory name.
  825. XThe additional capabilities this provides are that this symbol is
  826. Xa true logical and thus can also be supplied to a directory command
  827. Xor used as a preface to a file name.
  828. X.sh 4 "E(X)TEND"
  829. X.pp
  830. XExtend provides a capability for a user to run from the standard DCL
  831. Xshell and yet extend to some Unix-like advanced capabilities when
  832. Xnecessary.  Extend provides the capability for easy i/o redirection,
  833. Xpipes, backgrounding, and multiple command entry from one command line.
  834. XExtend is invoked by simply typing \&'x' followed by a list of dcl
  835. Xcommands mixed with extended command characters.
  836. X.ip "input redirection"
  837. XInput can be obtained from files on the system through the use of
  838. Xthe \&'< file' command.  This command works by both assigning sys$input and
  839. Xpas$input and specifing the file name as the last parameter to the command.
  840. XA unix mode \&'-u' for extend will eliminate the file specification.  This works
  841. Xin most cases but not always.
  842. X.ip "output redirection"
  843. XOutput can be diverted to a file on the system through the use of the
  844. X\&'> file' command.  This command will divert sys$output and pas$output
  845. Xto the file.
  846. X.ip "pipes"
  847. XTwo commands can have their outputs and inputs connected through the
  848. Xpipe construct \&'|'.  The effect is to do an output redirection on
  849. Xthe source process and a input redirection on the target process
  850. Xso again, the input redirection is not guaranteed to work.  Pipes differ
  851. Xin that they use mailboxes for the connection, not files.
  852. X.ip "standard error"
  853. XMany times you want to divert both sys$output and sys$error.  In Unix,
  854. Xstderr holds only error messages and stdout has the rest.  In VMS,
  855. Xsys$output is basically a superset of sys$error.  Therefore, while
  856. X\&'>&' and \&'|&' to divert both sys$output and sys$error are supported,
  857. Xthe effect is to divert sys$output and send sys$error to the null
  858. Xdevice.
  859. X.ip "multiple commands"
  860. XMultiple commands on a line may be simply specified by separating them
  861. Xby semicolons ';'.
  862. X.ip "backgrounding"
  863. XYou can background a command by simply following the command specification
  864. Xby a \&'&'.  To check the status of the command there is a separate command
  865. X\&'jobs' which will show the status of background processes.
  866. XAborting the execution of the command is possible using the
  867. X\&'kill' command.  \&'kill' takes one argument, either the
  868. Xprocess id to kill or the job number preceeded by a \&'%'.
  869. XYou can select whether you want immediate notification of process
  870. Xcompletion or only when executing the \&'jobs', \&'kill'
  871. Xor \&'x' commands,
  872. Xset a logical called 'notify' in the DCL shell to any value if
  873. Xyou want immediate notification.
  874. X.ip "grouping"
  875. XYou can group commands so that an i/o redirection will apply
  876. Xto the entire sequence of commands by surrounding the commands with
  877. Xparentheses and following the parentheses with the redirection
  878. Xcommands.
  879. X.ip "quoting"
  880. XIf you want to pass one of these special characters to the DCL shell,
  881. Xsuch as \&';' for a version number, you must 'quote' it by preceeding
  882. Xit with the \&'\' character.
  883. /
  884. echo 'x - x.c'
  885. sed 's/^X//' > x.c << '/'
  886. X/*
  887. X
  888. X    e(x)tend - David C. Albrecht 4/4/86
  889. X
  890. X    Extend is called from the VMS shell and provides some of the
  891. X    functionality Unix (TM), namely i/o redirection, pipes and back-
  892. X    grounding of processes.  Normally the user won't need these facilities
  893. X    and can thus run in the more efficient vanilla VMS environment.  It is
  894. X    quite simple, however, when the need for the extensions arises to
  895. X    invoke extend for one extended command.
  896. X
  897. X    Extend supports:
  898. X
  899. X    i/o redirection:
  900. X        >    redirect sys$output to a file.
  901. X        <    get sys$input from a file AND put the file name as
  902. X        the last parameter of the next invoked routine.
  903. X        >&  redirect sys$output to a file and send sys$error to
  904. X        the null device.
  905. X
  906. X    pipes:
  907. X        |   spawn two processes and attach a mailbox between them.
  908. X        works like > for the source process and like < for the
  909. X        receiving process.
  910. X        |&    spawn two processes and attach a mailbox between them.
  911. X        works like >& for the source process and like < for the
  912. X        receiving process.
  913. X
  914. X    multiple commands per line:
  915. X        ;   a semicolon will separate multiple commands which are
  916. X        to be processed serially.
  917. X
  918. X    backgrounding of processes:
  919. X        &   will indicate that the preceeding command sequence is
  920. X        to be placed to run in the background.  & also acts
  921. X        as a command separator.
  922. X
  923. X    process grouping
  924. X        ()    will group jobs so that the previous commands will
  925. X        apply to them as a group.
  926. X
  927. X    all these characters are treated as above unless escaped by a preceeding
  928. X    backslash character (\).
  929. X
  930. X    A companion program (jobs) is available which will display current
  931. X    status of a background process.
  932. X
  933. X    Note that two modes for background jobs is available:
  934. X
  935. X    If a logical named 'notify' is defined in the DCL shell.
  936. X        The subprocess will signal completion immediately by
  937. X        writing a completion message to the terminal.
  938. X
  939. X    If the logical is not set then.
  940. X        The subprocess will complete silently and completion
  941. X        will be signalled to the user when he runs either
  942. X        jobs or extend.
  943. X
  944. X*/
  945. X
  946. X#define NULL        0
  947. X#define SHORT_STR    40
  948. X#define MAXSTRING    150    /* max single comand size */
  949. X#define MAXCMDS        20    /* max vms commands in one extend command */
  950. X/*
  951. X    flag bits for a command
  952. X    and character process modes
  953. X*/
  954. X#define ARGUMENT    0
  955. X#define IN_FILE        1
  956. X#define OUT_FILE    2
  957. X#define PIPE_IN        4
  958. X#define PIPE_OUT    8
  959. X#define BG        16
  960. X#define ERROR_OUT    32
  961. X#define CLEAR_IN    64
  962. X#define CLEAR_OUT    128
  963. X#define CLEAR_ERROR    256
  964. X/*
  965. X    spawn command flag bits
  966. X*/
  967. X#define NO_WAIT 0x1
  968. X#define NOTIFY 0x10
  969. X/*
  970. X    vms interface files
  971. X*/
  972. X#include <rms.h>
  973. X#include <stdio.h>
  974. X#include <stsdef.h>
  975. X#include <ssdef.h>
  976. X#include <descrip.h>
  977. X
  978. XFILE *comfile;    /* file variable for VMS com file */
  979. X
  980. X/* descriptors for passing to vms commands */
  981. Xstruct dsc$descriptor_s desc,desc1;
  982. X
  983. X/*
  984. X    command description record
  985. X*/
  986. Xstruct cmd_desc_type {
  987. X    int state;        /* flag word indicating various info, see above */
  988. X    char *arg_str;    /* text of vms command */
  989. X    char *input;    /* input file */
  990. X    char *output;    /* output file */
  991. X    } cmd_desc[MAXCMDS];
  992. X
  993. Xint cmd_cnt;        /* number of vms commands */
  994. X
  995. Xint paren_in_process = -1;    /* cmd_cnt number for beginning of
  996. X                    paren block in process */
  997. X
  998. Xmain (argc,argv)
  999. X    int argc;
  1000. X    char **argv;
  1001. X
  1002. X{   int i,j;
  1003. X    int unix = FALSE;    /* unix style */
  1004. X    int keep_comfile = FALSE;    /* delete comfile after execution */
  1005. X    int flags;        /* flags to lib$spawn */
  1006. X    int pid;        /* current process id */
  1007. X    int job_sequence = FALSE;    /* flag indicating a serial job sequence */
  1008. X    int next_job;    /* subprocess number of job */
  1009. X    char str[MAXSTRING];    /* multi-purpose:
  1010. X                    extend arguments
  1011. X                    vms com file names
  1012. X                */
  1013. X    char exec_str[SHORT_STR];    /* com file execution text */
  1014. X    char jobstr[SHORT_STR];    /* current bg jobs indicator string */
  1015. X    char title[MAXSTRING];    /* text of title for current stage of
  1016. X                   background process
  1017. X                */
  1018. X    char job_title[MAXSTRING];    /* text of title for completed background
  1019. X                   process
  1020. X                */
  1021. X    int backgrounded_process = FALSE;    /* backgrounded process flag */
  1022. X
  1023. X    /*
  1024. X    get process id
  1025. X    */
  1026. X    pid = getpid();
  1027. X
  1028. X    /*
  1029. X    get background job process string
  1030. X    */
  1031. X    if (getsymbol("JOB_STATUS",str)) {
  1032. X    strcpy(jobstr,str);
  1033. X    /*
  1034. X        update any completed background jobs
  1035. X    */
  1036. X    update_job_status(jobstr,&next_job);
  1037. X    }
  1038. X    else {
  1039. X    *jobstr = '\0';
  1040. X    next_job = 1;
  1041. X    }
  1042. X    /*
  1043. X    set updated job process string
  1044. X    */
  1045. X    if (*jobstr) {
  1046. X    setsymbol("JOB_STATUS",jobstr);
  1047. X    }
  1048. X    else {
  1049. X    delsymbol("JOB_STATUS");
  1050. X    }
  1051. X
  1052. X    if (argc <= 1) exit(SS$_NORMAL);
  1053. X    /*
  1054. X    concatenate arguments into one string
  1055. X    */
  1056. X    *str = '\0';
  1057. X    i = 1;
  1058. X    if (!strcmp(argv[i],"-u")) {
  1059. X    i++;
  1060. X    unix = TRUE;
  1061. X    }
  1062. X    if (!strcmp(argv[i],"-k")) {
  1063. X    i++;
  1064. X    keep_comfile = TRUE;
  1065. X    }
  1066. X    for (; i < argc; i++) {
  1067. X    if (*str) {
  1068. X       strcat(str," ");
  1069. X    }
  1070. X    strcat(str,argv[i]);
  1071. X    }
  1072. X    /*
  1073. X    process string into commands
  1074. X    */
  1075. X    if (process_arg(str)) {
  1076. X    return(1);
  1077. X    }
  1078. X    /*
  1079. X    init flag for sequential sequence of commands
  1080. X    */
  1081. X    for (i = 0; i < cmd_cnt; i++) {
  1082. X    if (!job_sequence) {
  1083. X        /*
  1084. X        open a new VMS com file
  1085. X        */
  1086. X        sprintf(str,"SYS$LOGIN:EXE%0d%0d.COM",pid,i);
  1087. X        comfile = fopen(str, "w");
  1088. X        if (comfile == NULL) {
  1089. X        sprintf(str,"EXE%0d%0d.COM",pid,i);
  1090. X        comfile = fopen(str, "w");
  1091. X        if (comfile == NULL) {
  1092. X            fprintf(stderr,"unable to open process file %s\n",str);
  1093. X            return(1);
  1094. X        }
  1095. X        }
  1096. X        /*
  1097. X        error exit line
  1098. X        */
  1099. X        fprintf(comfile,"$ on error then goto exit\n");
  1100. X        /*
  1101. X        null the job completion title
  1102. X        */ 
  1103. X        *job_title = '\0';
  1104. X    }
  1105. X    else {
  1106. X        job_sequence = FALSE;
  1107. X    }
  1108. X    /*
  1109. X        set title to name of current vms command
  1110. X    */
  1111. X        strcpy(title,cmd_desc[i].arg_str);
  1112. X    /*
  1113. X       append names of following sequential vms commands
  1114. X    */
  1115. X    j = i + 1;
  1116. X    while (j < cmd_cnt && !(cmd_desc[j-1].state & BG)) {
  1117. X        strcat(title," ; ");
  1118. X        strcat(title,cmd_desc[j].arg_str);
  1119. X        j++;
  1120. X    }
  1121. X    /*
  1122. X       first time, save title as the job completion title
  1123. X    */
  1124. X    if (!*job_title) {
  1125. X        strcpy(job_title,title);
  1126. X    }
  1127. X    /*
  1128. X       if an asynchronous completing job then generate line to
  1129. X       update job status in the vms com file
  1130. X    */
  1131. X    if (cmd_desc[cmd_cnt - 1].state & BG) {
  1132. X        fprintf(comfile,
  1133. X            "$ define/job/nol JOB_%d \"''f$getjpi(0,\"PID\")' %s\"\n",
  1134. X            next_job,title);
  1135. X    }
  1136. X    /*
  1137. X       unredirect sys$input
  1138. X    */
  1139. X    if (cmd_desc[i].state & CLEAR_IN) {
  1140. X        fprintf(comfile,"$ deassign sys$input\n");
  1141. X        fprintf(comfile,"$ deassign pas$input\n");
  1142. X    }
  1143. X    /*
  1144. X       unredirect sys$output and sys$error
  1145. X    */
  1146. X    if (cmd_desc[i].state & CLEAR_OUT) {
  1147. X        fprintf(comfile,"$ deassign sys$output\n");
  1148. X        fprintf(comfile,"$ deassign pas$output\n");
  1149. X    }
  1150. X    if (cmd_desc[i].state & CLEAR_ERROR) {
  1151. X        fprintf(comfile,"$ deassign sys$error\n");
  1152. X    }
  1153. X    /*
  1154. X        redirect output streams for output pipes or files
  1155. X    */
  1156. X    if (cmd_desc[i].state & (OUT_FILE | PIPE_OUT)) {
  1157. X        fprintf(comfile,"$ define sys$output %s\n",cmd_desc[i].output);
  1158. X        fprintf(comfile,"$ define pas$output %s\n",cmd_desc[i].output);
  1159. X        /*
  1160. X        when sys$error is redirected throw it away by
  1161. X        sending it to the null device since sys$output
  1162. X        is a superset of sys$error
  1163. X        */
  1164. X        if (cmd_desc[i].state & ERROR_OUT) {
  1165. X        if (unix) {
  1166. X            fprintf(comfile,"$ define sys$error %s\n",
  1167. X                    cmd_desc[i].output);
  1168. X        }
  1169. X        else {
  1170. X            fprintf(comfile,"$ define sys$error nl:\n");
  1171. X        }
  1172. X        }
  1173. X    }
  1174. X    /*
  1175. X        redirect input streams for input pipes or file
  1176. X        in addition, put input file as last parameter
  1177. X        to associated vms command.
  1178. X    */
  1179. X    if (cmd_desc[i].state & (IN_FILE | PIPE_IN)) {
  1180. X        fprintf(comfile,"$ define/nolog sys$input %s\n",cmd_desc[i].input);
  1181. X        fprintf(comfile,"$ define pas$input %s\n",cmd_desc[i].input);
  1182. X        if (unix) {
  1183. X        fprintf(comfile,"$ %s\n",cmd_desc[i].arg_str);
  1184. X        }
  1185. X        else {
  1186. X        fprintf(comfile,"$ %s %s\n",cmd_desc[i].arg_str,cmd_desc[i].input);
  1187. X        }
  1188. X    }
  1189. X    else {
  1190. X        fprintf(comfile,"$ %s\n",cmd_desc[i].arg_str);
  1191. X    }
  1192. X    /*
  1193. X        if job is not being backgrounded,
  1194. X        and there is a following job then,
  1195. X        set the job sequence flag
  1196. X    */
  1197. X    if (!(cmd_desc[i].state & BG)) {
  1198. X        if (i+1 < cmd_cnt) {
  1199. X        job_sequence = TRUE;
  1200. X        }
  1201. X    }
  1202. X    if (!job_sequence) {
  1203. X        /*
  1204. X        finish off vms com file
  1205. X        */
  1206. X        fprintf(comfile,"$ exit:\n");
  1207. X        strcpy(exec_str,"@");
  1208. X        strcat(exec_str,str);
  1209. X        setdesc(&desc,exec_str,strlen(exec_str));
  1210. X        if (!(cmd_desc[i].state & BG)) {
  1211. X        flags = NULL;
  1212. X        }
  1213. X        else {
  1214. X        flags = NO_WAIT;
  1215. X        if (!(cmd_desc[i].state & PIPE_OUT)) {
  1216. X            backgrounded_process = TRUE;
  1217. X            /*
  1218. X            when asynchronous execution add file completion
  1219. X            and status update lines to vms com file
  1220. X            */
  1221. X            fprintf(comfile,
  1222. X    "$ if \"''f$logical(\"NOTIFY\")'\" .nes. \"\" then goto speak\n");
  1223. X            fprintf(comfile,"$ define/job/nol JOB_%D \"DONE %s\"\n",
  1224. X                    next_job,job_title);
  1225. X            if (!keep_comfile) {
  1226. X            fprintf(comfile,"$ delete %s;*\n",str);
  1227. X            }
  1228. X            fprintf(comfile,"$ exit\n");
  1229. X            fprintf(comfile,"$ speak:\n");
  1230. X            fprintf(comfile,"$ define/job/nol JOB_%D \"DONE\"\n",
  1231. X                    next_job);
  1232. X            printf("[%d] %s\n",next_job ,job_title);
  1233. X            fprintf(comfile,"$ deassign sys$output\n");
  1234. X            fprintf(comfile,"$ write sys$output \"[%d] DONE %s\"\n",
  1235. X                    next_job,job_title);
  1236. X            /*
  1237. X            update background job status string
  1238. X            */
  1239. X            strcat(jobstr,"X");
  1240. X            next_job++;
  1241. X        }
  1242. X        }
  1243. X        if (!keep_comfile) {
  1244. X         fprintf(comfile,"$ delete %s;*\n",str);
  1245. X        }
  1246. X        fclose(comfile);
  1247. X        /*
  1248. X        spawn the process for execution
  1249. X        */
  1250. X        lib$spawn(&desc,0,0,&flags,0,0,0,0,0,0,0,0);
  1251. X    }
  1252. X    }
  1253. X    if (backgrounded_process) {
  1254. X    /*
  1255. X        if asynchronous job execution then set the background
  1256. X        job status symbol on exit
  1257. X    */
  1258. X    setsymbol("JOB_STATUS",jobstr);
  1259. X    }
  1260. X}
  1261. X/*
  1262. X    add_command
  1263. X    add a command to the command description structure
  1264. X*/
  1265. Xadd_command(cmd_desc_pt,cmd_cnt_pt,state,arg_str,input,output)
  1266. Xstruct cmd_desc_type (*cmd_desc_pt)[];
  1267. Xint *cmd_cnt_pt,*state;
  1268. Xchar *arg_str,*input,*output;
  1269. X{   struct cmd_desc_type *cmd_desc_entry;
  1270. X
  1271. X    if ((*state & (IN_FILE | OUT_FILE | PIPE_IN | PIPE_OUT)) && !arg_str) {
  1272. X    fprintf(stderr,"invalid i/o redirection\n");
  1273. X    return(1);
  1274. X    }
  1275. X    if (!*arg_str) {
  1276. X    return(0);
  1277. X    }
  1278. X    /*
  1279. X    get a pointer to the current command descripion record
  1280. X    */
  1281. X    cmd_desc_entry = &((*cmd_desc_pt)[(*cmd_cnt_pt)++]);
  1282. X    /*
  1283. X    set the state, argument, input, and output fields and re-init
  1284. X    */
  1285. X    cmd_desc_entry->state = *state;
  1286. X    *state = NULL;
  1287. X    if (*arg_str) {
  1288. X    cmd_desc_entry->arg_str = malloc(strlen(arg_str)+1);
  1289. X    strcpy(cmd_desc_entry->arg_str,arg_str);
  1290. X    *arg_str = '\0';
  1291. X    }
  1292. X    if (*input) {
  1293. X    cmd_desc_entry->input = malloc(strlen(input)+1);
  1294. X    strcpy(cmd_desc_entry->input,input);
  1295. X    *input = '\0';
  1296. X    }
  1297. X    if (*output) {
  1298. X    cmd_desc_entry->output = malloc(strlen(output)+1);
  1299. X    strcpy(cmd_desc_entry->output,output);
  1300. X    *output = '\0';
  1301. X    }
  1302. X    /*
  1303. X    initialize the next description record
  1304. X    */
  1305. X    init_cmd_desc(&((*cmd_desc_pt)[*cmd_cnt_pt]));
  1306. X    return(0);
  1307. X
  1308. X}
  1309. X
  1310. X/*
  1311. X    check_mode
  1312. X    check mode checks the current and next mode,
  1313. X    command state, input and output, and validates and sets
  1314. X    the new mode.
  1315. X*/
  1316. Xcheck_mode(curr_mode,new_mode,state,input,output)
  1317. Xint curr_mode,new_mode,*state;
  1318. Xchar *input,*output;
  1319. X{
  1320. X    if (curr_mode == IN_FILE) {
  1321. X    if (!*input) {
  1322. X        fprintf(stderr,"null redirection attempted\n");
  1323. X        return(-1);
  1324. X    }
  1325. X    else if (paren_in_process >= 0) {
  1326. X        cmd_desc[paren_in_process].input = malloc(strlen(input)+1);
  1327. X        strcpy(cmd_desc[paren_in_process].input, input);
  1328. X        *input = '\0';
  1329. X    }
  1330. X    }
  1331. X    if (curr_mode == OUT_FILE) {
  1332. X    if (!*output) {
  1333. X        fprintf(stderr,"null redirection attempted\n");
  1334. X        return(-1);
  1335. X    }
  1336. X    else if (paren_in_process >= 0) {
  1337. X        cmd_desc[paren_in_process].output = malloc(strlen(output)+1);
  1338. X        strcpy(cmd_desc[paren_in_process].output, output);
  1339. X        *output = '\0';
  1340. X    }
  1341. X    }
  1342. X    if (new_mode & (IN_FILE | PIPE_IN)) {
  1343. X    if (*state & (IN_FILE | PIPE_IN)) {
  1344. X        fprintf(stderr,"multiple redirection attempted\n");
  1345. X        return(-1);
  1346. X    }
  1347. X    else {
  1348. X        *state |= new_mode;
  1349. X    }
  1350. X    }
  1351. X    else if (new_mode & (OUT_FILE | PIPE_OUT)) {
  1352. X    if (*state & (OUT_FILE | PIPE_OUT)) {
  1353. X        fprintf(stderr,"multiple redirection attempted\n");
  1354. X        return(-1);
  1355. X    }
  1356. X    else {
  1357. X        *state |= new_mode;
  1358. X    }
  1359. X    }
  1360. X    return(new_mode);
  1361. X}
  1362. X
  1363. X/*
  1364. X    dellogical
  1365. X    delete a logical from the job table
  1366. X*/
  1367. Xdellogical(logical_name)
  1368. Xchar *logical_name;
  1369. X{   char job_table[20];
  1370. X
  1371. X    setdesc(&desc,logical_name,strlen(logical_name));
  1372. X    sprintf(job_table,"LNM$JOB_%d",getuid());
  1373. X    setdesc(&desc1,job_table,strlen(job_table));
  1374. X    lib$delete_logical(&desc,desc1);
  1375. X}
  1376. X
  1377. X/*
  1378. X    delsymbol
  1379. X    delete a symbol from the global level
  1380. X*/
  1381. Xdelsymbol(symbol_name)
  1382. Xchar *symbol_name;
  1383. X{   int tbl;
  1384. X
  1385. X    setdesc(&desc,symbol_name,strlen(symbol_name));
  1386. X    tbl = 2;
  1387. X    lib$delete_symbol(&desc,&tbl);
  1388. X}
  1389. X
  1390. X/*
  1391. X    getlogical
  1392. X    get a logical translation
  1393. X*/
  1394. Xgetlogical(logical_name,value)
  1395. Xchar *logical_name,*value;
  1396. X{   int valuelen;
  1397. X
  1398. X    setdesc(&desc,logical_name,strlen(logical_name));
  1399. X    setdesc(&desc1,value,MAXSTRING-1);
  1400. X    valuelen = 0;
  1401. X    lib$sys_trnlog(&desc,&valuelen,&desc1,0,0,0);
  1402. X    value[valuelen] = '\0';
  1403. X    if (!strcmp(logical_name,value)) {
  1404. X    return(0);
  1405. X    }
  1406. X    return(1);
  1407. X
  1408. X}
  1409. X
  1410. X/*
  1411. X    getsymbol
  1412. X    get a symbol value
  1413. X*/
  1414. Xgetsymbol(symbol_name,value)
  1415. Xchar *symbol_name,*value;
  1416. X{   int valuelen,status;
  1417. X
  1418. X    setdesc(&desc,symbol_name,strlen(symbol_name));
  1419. X    setdesc(&desc1,value,MAXSTRING-1);
  1420. X    valuelen = 0;
  1421. X    status = lib$get_symbol(&desc,&desc1,&valuelen,0);
  1422. X    if (status & STS$M_SUCCESS) {
  1423. X    value[valuelen] = '\0';
  1424. X    return(1);
  1425. X    }
  1426. X    else {
  1427. X    return(0);
  1428. X    }
  1429. X}
  1430. X
  1431. X/*
  1432. X   get_pipe
  1433. X   create a pipe_name and open a mailbox under that name
  1434. X*/
  1435. Xget_pipe(pipe_name,pipeno)
  1436. Xchar *pipe_name;
  1437. Xint pipeno;
  1438. X{   int i,chan;
  1439. X
  1440. X    i = getpid();
  1441. X    sprintf(pipe_name,"COM%0d%0d",i,pipeno);
  1442. X    setdesc(&desc,pipe_name,strlen(pipe_name));
  1443. X    sys$crembx(0,&chan,1024,2048,0xff0f,0,&desc);
  1444. X}
  1445. X
  1446. X/*
  1447. X    init_cmd_desc
  1448. X    initialize a command description entry
  1449. X*/
  1450. Xinit_cmd_desc(cmd_desc_entry)
  1451. Xstruct cmd_desc_type *cmd_desc_entry;
  1452. X{
  1453. X    cmd_desc_entry->state = NULL;
  1454. X    cmd_desc_entry->arg_str = NULL;
  1455. X    cmd_desc_entry->input = NULL;
  1456. X    cmd_desc_entry->output = NULL;
  1457. X}
  1458. X
  1459. X/*
  1460. X    process_arg
  1461. X    split the extend argument string and process into separate commands.
  1462. X*/
  1463. Xprocess_arg(str)
  1464. Xchar *str;
  1465. X{   /*
  1466. X    state,arg_str,input,output
  1467. X    are local temporaries for the corresponding fields in a
  1468. X    command description record.
  1469. X    */
  1470. X    int state = NULL;
  1471. X    char arg_str[MAXSTRING];
  1472. X    char input[MAXSTRING];
  1473. X    char output[MAXSTRING];
  1474. X    int old_state = NULL;    /* old state values */
  1475. X    char cstr[2],c;    /* temporary character manipulation storage */
  1476. X    int char_mode = ARGUMENT;    /* next character addition mode */
  1477. X
  1478. X    int parens = 0;        /* paren levels saved */
  1479. X    int paren_levels[4];    /* paren level storage */
  1480. X
  1481. X    int i;
  1482. X
  1483. X    /*
  1484. X    init the command description temporaries
  1485. X    */
  1486. X    *arg_str = '\0';
  1487. X    *input = '\0';
  1488. X    *output = '\0';
  1489. X    /*
  1490. X    zero the number of commands and init the first command
  1491. X    description record
  1492. X    */
  1493. X    cmd_cnt = 0;
  1494. X    init_cmd_desc(&cmd_desc[cmd_cnt]);
  1495. X
  1496. X    while (c = *str++) {
  1497. X    /* command separator */
  1498. X    if (c == ';') {
  1499. X        if ((char_mode =
  1500. X            check_mode(char_mode,ARGUMENT,&state,input,output)) < 0) {
  1501. X        return(1);
  1502. X        }
  1503. X        old_state = state;
  1504. X        if (add_command(cmd_desc,&cmd_cnt,&state,arg_str,input,output)) {
  1505. X        return(1);
  1506. X        }
  1507. X        if (old_state & OUT_FILE) state |= CLEAR_OUT;
  1508. X        if (old_state & IN_FILE) state |= CLEAR_IN;
  1509. X        if (old_state & ERROR_OUT) state |= CLEAR_ERROR;
  1510. X        paren_in_process = -1;
  1511. X    }
  1512. X    /* pipe */
  1513. X    else if (c == '|') {
  1514. X        if (paren_in_process < 0) {        
  1515. X        if ((char_mode =
  1516. X            check_mode(char_mode,PIPE_OUT,&state,input,output)) < 0) {
  1517. X            return(1);
  1518. X        }
  1519. X        get_pipe(output);
  1520. X        if (*str == '&') {
  1521. X            str++;
  1522. X            state |= ERROR_OUT;
  1523. X        }
  1524. X        if (add_command(cmd_desc,&cmd_cnt,&state,arg_str,input,output)) {
  1525. X            return(1);
  1526. X        }
  1527. X        cmd_desc[cmd_cnt - 1].state |= BG;
  1528. X        /*
  1529. X            set up pipe input to next command
  1530. X        */
  1531. X        if ((char_mode =
  1532. X            check_mode(char_mode,PIPE_IN,&state,input,output)) < 0 ) {
  1533. X            return(1);
  1534. X        }
  1535. X        cmd_desc[cmd_cnt].input = cmd_desc[cmd_cnt - 1].output;
  1536. X        char_mode = check_mode(char_mode,ARGUMENT,&state,input,output);
  1537. X        }
  1538. X        else {
  1539. X        if ((char_mode =
  1540. X            check_mode(char_mode,PIPE_OUT,
  1541. X                    &cmd_desc[paren_in_process].state,
  1542. X                    cmd_desc[paren_in_process].input,
  1543. X                    cmd_desc[paren_in_process].output)) < 0) {
  1544. X            return(1);
  1545. X        }
  1546. X        for (i = paren_in_process + 1; i < cmd_cnt; i++) {
  1547. X            if (cmd_desc[i].state & (OUT_FILE | PIPE_OUT)) {
  1548. X            fprintf(stderr, "multiple redirection attempted\n");
  1549. X            return(1);
  1550. X            }
  1551. X        }
  1552. X        if (state & OUT_FILE) {
  1553. X            fprintf(stderr, "multiple redirection attempted\n");
  1554. X            return(1);
  1555. X        }
  1556. X        get_pipe(output);
  1557. X        cmd_desc[paren_in_process].output = malloc(strlen(output)+1);
  1558. X        strcpy(cmd_desc[paren_in_process].output, output);
  1559. X        *output = '\0';
  1560. X        if (*str == '&') {
  1561. X            str++;
  1562. X            cmd_desc[paren_in_process].state |= ERROR_OUT;
  1563. X        }
  1564. X        if (add_command(cmd_desc,&cmd_cnt,&state,arg_str,input,output)) {
  1565. X            return(1);
  1566. X        }
  1567. X        cmd_desc[cmd_cnt - 1].state |= BG;
  1568. X        /*
  1569. X            set up pipe input to next command
  1570. X        */
  1571. X        if ((char_mode =
  1572. X            check_mode(char_mode,PIPE_IN,&state,input,output)) < 0) {
  1573. X            return(1);
  1574. X        }
  1575. X        cmd_desc[cmd_cnt].input = cmd_desc[paren_in_process].output;
  1576. X        char_mode = check_mode(char_mode,ARGUMENT,&state,input,output);
  1577. X        paren_in_process = -1;
  1578. X        }
  1579. X    }
  1580. X    /* background */
  1581. X    else if (c == '&') {
  1582. X        if ((char_mode =
  1583. X            check_mode(char_mode,ARGUMENT,&state,input,output)) < 0) {
  1584. X        return(1);
  1585. X        }
  1586. X        if (add_command(cmd_desc,&cmd_cnt,&state,arg_str,input,output)) {
  1587. X        return(1);
  1588. X        }
  1589. X        cmd_desc[cmd_cnt - 1].state |= BG;
  1590. X        paren_in_process = -1;
  1591. X    }
  1592. X    /* redirect output */
  1593. X    else if (c == '>') {
  1594. X        if (paren_in_process < 0) {
  1595. X        if ((char_mode =
  1596. X            check_mode(char_mode,OUT_FILE,&state,input,output)) < 0) {
  1597. X            return(1);
  1598. X        }
  1599. X        if (*str == '&') {
  1600. X            str++;
  1601. X            state |= ERROR_OUT;
  1602. X        }
  1603. X        }
  1604. X        else {
  1605. X        if ((char_mode =
  1606. X            check_mode(char_mode,OUT_FILE,
  1607. X                    &cmd_desc[paren_in_process].state,
  1608. X                    cmd_desc[paren_in_process].input,
  1609. X                    cmd_desc[paren_in_process].output)) < 0) {
  1610. X            return(1);
  1611. X        }
  1612. X        for (i = paren_in_process + 1; i < cmd_cnt; i++) {
  1613. X            if (cmd_desc[i].state & (OUT_FILE | PIPE_OUT)) {
  1614. X            fprintf(stderr, "multiple redirection attempted\n");
  1615. X            return(1);
  1616. X            }
  1617. X        }
  1618. X        if (state & OUT_FILE) {
  1619. X            fprintf(stderr, "multiple redirection attempted\n");
  1620. X            return(1);
  1621. X        }
  1622. X        if (*str == '&') {
  1623. X            str++;
  1624. X            cmd_desc[paren_in_process].state |= ERROR_OUT;
  1625. X        }
  1626. X        }
  1627. X    }
  1628. X    /* redirect input */
  1629. X    else if (c == '<') {
  1630. X        if (paren_in_process < 0) {
  1631. X        if ((char_mode =
  1632. X            check_mode(char_mode,IN_FILE,&state,input,output)) < 0) {
  1633. X            return(1);
  1634. X        }
  1635. X        }
  1636. X        else {
  1637. X        if ((char_mode =
  1638. X            check_mode(char_mode,IN_FILE,
  1639. X                    &cmd_desc[paren_in_process].state,
  1640. X                    cmd_desc[paren_in_process].input,
  1641. X                    cmd_desc[paren_in_process].output)) < 0) {
  1642. X            return(1);
  1643. X        }
  1644. X        for (i = paren_in_process + 1; i < cmd_cnt; i++) {
  1645. X            if (cmd_desc[i].state & (IN_FILE | PIPE_IN)) {
  1646. X            fprintf(stderr, "multiple redirection attempted\n");
  1647. X            return(1);
  1648. X            }
  1649. X        }
  1650. X        if (state & (IN_FILE | PIPE_IN)) {
  1651. X            fprintf(stderr, "multiple redirection attempted\n");
  1652. X            return(1);
  1653. X        }
  1654. X        }
  1655. X    }
  1656. X    /* job group start */
  1657. X    else if (c == '(') {
  1658. X        if (paren_in_process >= 0 || char_mode != ARGUMENT || *arg_str) {
  1659. X        fprintf(stderr,"invalid job grouping\n");
  1660. X        return(1);
  1661. X        }
  1662. X        paren_levels[parens++] = cmd_cnt;
  1663. X    }
  1664. X    /* job group end */
  1665. X    else if (c == ')') {
  1666. X        if (!parens) {
  1667. X        fprintf(stderr,"mis-matched parenthisis\n");
  1668. X        return(1);
  1669. X        }
  1670. X        if (char_mode != ARGUMENT || !(*arg_str)) {
  1671. X        fprintf(stderr,"invalid job grouping\n");
  1672. X        return(1);
  1673. X        }
  1674. X        paren_in_process = paren_levels[--parens];
  1675. X    }
  1676. X    else {
  1677. X        /* escaping of next character */
  1678. X        if (c == '\\') {
  1679. X        if (!(*cstr = *str++)) {
  1680. X            return(1);
  1681. X        }
  1682. X        }
  1683. X        /* any other character */
  1684. X        else {
  1685. X        /*
  1686. X            space or tab delimits a file name in file redirection
  1687. X            modes, reset the character mode to ARGUMENT input.
  1688. X        */
  1689. X        if (c == ' ' || c == '\t') {
  1690. X            if ((char_mode == IN_FILE && *input)
  1691. X             || (char_mode == OUT_FILE && *output)) {
  1692. X            if ((char_mode =
  1693. X                check_mode(char_mode,ARGUMENT,
  1694. X                       &state,input,output)) < 0) {
  1695. X                return(1);
  1696. X            }
  1697. X                continue;
  1698. X            }
  1699. X        }
  1700. X        *cstr = c;
  1701. X        }
  1702. X        *(cstr+1) = '\0';
  1703. X        
  1704. X        /*
  1705. X        three character modes are available for normal character
  1706. X        ARGUMENT - part of a vms command
  1707. X        IN_FILE  - part of a input redirection file name
  1708. X        OUT_FILE - part of an output redirection file name
  1709. X        */
  1710. X        switch(char_mode) {
  1711. X
  1712. X        case ARGUMENT:
  1713. X            strcat(arg_str,cstr);
  1714. X            break;
  1715. X
  1716. X        case IN_FILE:
  1717. X            strcat(input,cstr);
  1718. X            break;
  1719. X
  1720. X        case OUT_FILE:
  1721. X            strcat(output,cstr);
  1722. X            break;
  1723. X
  1724. X        }        
  1725. X    }
  1726. X    }
  1727. X    /*
  1728. X    exit cleanup
  1729. X    */
  1730. X    if (check_mode(char_mode,ARGUMENT,&state,input,output) < 0) {
  1731. X    return(1);
  1732. X    }
  1733. X    if (add_command(cmd_desc,&cmd_cnt,&state,arg_str,input,output)) {
  1734. X    return(1);
  1735. X    }
  1736. X    return(0);
  1737. X}
  1738. X/*
  1739. X    setdesc
  1740. X    set descriptor to a standard ascii string
  1741. X*/
  1742. Xsetdesc(descr,str,strlen)
  1743. Xstruct dsc$descriptor_s *descr;
  1744. Xchar *str;
  1745. Xint strlen;
  1746. X{
  1747. X    descr->dsc$w_length = strlen;
  1748. X    descr->dsc$a_pointer = str;
  1749. X    descr->dsc$b_class = DSC$K_CLASS_S;    /* String desc class */
  1750. X    descr->dsc$b_dtype = DSC$K_DTYPE_T;    /* Ascii string type */
  1751. X}
  1752. X/*
  1753. X    setsymbol
  1754. X    set a symbol in the global table
  1755. X*/
  1756. Xsetsymbol(symbol_name,value)
  1757. Xchar *symbol_name,*value;
  1758. X{   int tbl;
  1759. X
  1760. X    setdesc(&desc,symbol_name,strlen(symbol_name));
  1761. X    setdesc(&desc1,value,strlen(value));
  1762. X    tbl = 2;
  1763. X    lib$set_symbol(&desc,&desc1,&tbl);
  1764. X}
  1765. X/*
  1766. X    update_job_status
  1767. X    update the status of any completed background jobs.
  1768. X    print completion code to the screen for any non-immediate
  1769. X    notification style jobs.
  1770. X*/
  1771. Xupdate_job_status(active_jobs,next_job)
  1772. Xchar *active_jobs;
  1773. Xint *next_job;
  1774. X{   int i;
  1775. X    char joblogical[MAXSTRING],status_info[MAXSTRING],*active;
  1776. X
  1777. X    /*
  1778. X    there is an 'X' in the active_jobs string for all background
  1779. X    jobs currently still outstanding.
  1780. X    */
  1781. X    for (i = 0; i < strlen(active_jobs); i++) {
  1782. X    if (active_jobs[i] != ' ') {
  1783. X        /*
  1784. X        each job maintains a JOB_'number' logical which
  1785. X        indicates current execute status of the job
  1786. X        */
  1787. X        sprintf(joblogical,"JOB_%d",i+1);
  1788. X        getlogical(joblogical,status_info);
  1789. X        /*
  1790. X        a symbol prefaced with DONE means the job is complete
  1791. X        */
  1792. X        if (!strncmp(status_info,"DONE",4)) {
  1793. X        if (strcmp(status_info,"DONE")) {
  1794. X            /*
  1795. X            if DONE is followed by additional info then
  1796. X            the job was not immediately notified so its
  1797. X            completion is signaled now
  1798. X            */
  1799. X            printf("[%d] %s\n",i+1,status_info);
  1800. X        }
  1801. X        /*
  1802. X            remove the completed job's logical
  1803. X        */
  1804. X        dellogical(joblogical);
  1805. X        /*
  1806. X            remove the active indicator from the active_jobs
  1807. X        */
  1808. X        active_jobs[i] = ' ';
  1809. X        }
  1810. X    }
  1811. X    }
  1812. X    /*
  1813. X    update active_jobs string to remove trailing blanks
  1814. X    */
  1815. X    active = active_jobs + strlen(active_jobs) - 1;
  1816. X    while (active >= active_jobs && *active == ' ') *(active--) = '\0';
  1817. X    /*
  1818. X    set number of next_job sub process
  1819. X    */
  1820. X    *next_job = strlen(active_jobs)+1;
  1821. X}
  1822. X/*
  1823. X    upshift
  1824. X    uppercase a character string
  1825. X*/
  1826. Xupshift(upname,lwname)
  1827. Xchar *upname,*lwname;
  1828. X{
  1829. X    while (*(upname++) = toupper(*(lwname++)));
  1830. X}
  1831. X
  1832. /
  1833. echo 'Part 01 of vms complete.'
  1834. exit
  1835.  
  1836.  
  1837.