A journey into the past: Programming with GNU COBOL

Latest version of this document | Read more articles

Back in early autumn 2015 my interest was sparked by a post on Slashdot that linked to an article on opensource.com. It discussed three Open Source projects relating to COBOL; a compile in GnuCOBOL, a COBOL IDE in OpenCOBOL IDE, and a Node.js to COBOL bridge.

Having been programming since the early eighties (BASIC on the Sinclair ZX-81 and ZX-Spectrum, Tandy TRS-80, Amstrad PCW, IBM PC and Atari ST) and then gone on to learn programming at University (Pascal, C, Ada, OCCAM), I was intrigued to find out that people were still writing COBOL and still actively developing tools for the language; after all as far as I knew the language was already considered a relic the Eighties. Most surprising was to find out that the latest official specification for the ISO standard of COBOL was COBOL 2014!

Having been doing it since the late 90s, I'd become a little bored with web development (or rather writing applications for the web) for some time. 20s years of doing it has lead to me being really not that excited by the latest you just struggle for ages with npm to get this amazing JavaScript framework installed and then look it's easy and amazing and also a little jaded at whether Python, or Ruby, or PHP, or Perl, or Drupal, or WordPress, or Node, or React.js, or Angular, or whatever, really is what I should be using to deliver yet another website for yet another person or company with year another great idea. I think you get the point.

So the idea of going back in time and back to basics and learning a programming language that had quite a different beginning and different paradigm to others I've used and one that I'd never looked at before really appealed to me. In addition, one that I could write straight-forward programs that had no layered APIs and frameworks and allowed me to interact directly with the screen, keyboard, and other input-output devices also appealed. So I thought to myself, 'why not learn COBOL?'; and that's just what I did!

A brief history COBOL

Briefly, because you can read about this on Wikipedia, COBOL was devised by CODASYL way back in 1958 and the first specification released in 1960. Grace Hopper (who is also purportedly the first person to debug a computer (there was literally a moth stuck in a relay of a Harvard Mark II computer)) is accredited as being the main influence behind COBOL through her previous work with FLOW-MATIC. Anyway, the long and the short of it was that it was a comittee formed of members from US government and industry that undertook the development of an open specification for a programming language that would be cross-platform.

By around 1974 COBOL was the most widely used programming language in the World and had become an ANSI standard (in fact was on it's third incarnation, COBOL-74). Since then there have been three subsequent major versions, ANSI-85, ISO-2002 and ISO-2014.

Anyway, as I said there's plenty of information on-line about that, so I won't go on.

COBOL basics

One of the key oddities of COBOL for programmers of other computer languages, and the computer science community in general, is that it's design principals, which included making the code human-readable, means that it does things in quite a different way to most languages, and therefore stands alone.

Hello World

Here's a version of the Hello World program written using OpenCOBOL IDE for COBOL 85. Note I used lower case syntax for keywords rather than the older use of UPPER CASE; either is acceptable in COBOL. The line numbers are there for reference and aren't part of the program.

  1. program-id. HelloWorld.
  2. procedure division.
  3. display "Hello World!".

Let's look at the same program using classic COBOL with upper case and line numbering:

00001 ******************************************************************
00002 * Author: Mike Harris
00003 * Date: 27th April 2016
00004 * Purpose: To demonstrate the Hello World program in COBOL
00005 * Compiler: cobc
00006 ******************************************************************
00010  IDENTIFICATION DIVISION.
00011  PROGRAM-ID. HELLO-WORLD.
00100  ENVIRONMENT DIVISION.
00110  PROCEDURE DIVISION.
00120  MAIN-PROCEDURE.
00130       DISPLAY "Hello world"
00140       STOP RUN.
00150  END PROGRAM HELLO-WORLD.
 

This format is called fixed format and was suitable for punch cards; other programming languages, such as FORTRAN, also originally used this style of format.

I prefer the more modern approach, called free format, and also prefer including some of the keywords that, although aren't required, do I think make the program more readable. For example:

  1. identification division.
  2. program-id. HelloWorld.
  3. procedure division.
  4. display "Hello World!"
  5. stop run
  6. .
  7. end program HelloWorld.

Monty Hall

Okay, let's now look at something bigger. The following is a possible solution to the Monty Hall problem. The listing is first and then I'll explain it to you:

  1. identification division.
  2. program-id. MontyHall.
  3.  
  4. data division.
  5. working-storage section.
  6.  
  7. 01 CurrentTime.
  8. 02 filler pic 9(4).
  9. 02 Seed pic 9(4).
  10.  
  11. 01 DoorThatHasCar pic 9.
  12. 01 DoorPlayerChooses pic 9.
  13.  
  14. 01 Round pic 9(4).
  15. 01 NumberOfRoundsToPlay constant 1000.
  16. 01 WinsWhenNotSwapping pic 9(3) value zero.
  17. 01 WinsWhenSwapping pic 9(3) value zero.
  18.  
  19. procedure division.
  20.  
  21. accept CurrentTime from time
  22. compute DoorThatHasCar = function random(Seed)
  23.  
  24. perform with test after varying Round from 1 by 1
  25. until Round equal to NumberOfRoundsToPlay
  26. compute DoorThatHasCar = (function random * 3) + 1
  27. compute DoorPlayerChooses = (function random * 3) + 1
  28. if DoorThatHasCar equal to DoorPlayerChooses then
  29. add 1 to WinsWhenNotSwapping
  30. else
  31. add 1 to WinsWhenSwapping
  32. end-if
  33. end-perform
  34.  
  35. display "Results"
  36. display "-------"
  37. display "After playing " Round " rounds ..."
  38. display "Times correct door selected after swapping: " WinsWhenSwapping
  39. display "Times correct door selected without swapping: " WinsWhenNotSwapping
  40. display spaces
  41.  
  42. evaluate true
  43. when WinsWhenNotSwapping greater than WinsWhenSwapping
  44. display
  45. "Conclusion is that it's better not to swap."
  46. " Which doesn't make much sense, but hey!"
  47. end-display
  48. when WinsWhenNotSwapping equal to WinsWhenSwapping
  49. display
  50. "There's no conclusion; they are equal outcomes."
  51. " Which shouldn't really be the case."
  52. end-display
  53. when other
  54. display
  55. "Conclusion is that it's better to swap."
  56. " This makes sense as the probability in the first round of getting"
  57. " the correct door is 1/3; whereas in the second, it's 1/2."
  58. end-display
  59. end-evaluate
  60.  
  61. stop run.
  62.  
  63. end program MontyHall.
  64.  

Putting it all together

I've been involved in running an event called BarnCamp for some years now. We had a website, built in Ruby-on-Rails, that captures sign-ups and can export them in CSV format. We needed some way on site of tracking who had arrived on what day, whether they'd paid and how much on site. In the past we'd used a spreadsheet for this, but in 2017 I decided to try an application dedicated to the task.

This application needed to solve the following needs:

I implemented the BarnCamp Attendees Management System (BAMS) in GNU COBOL to provide a solution for the event. This application used lots of COBOL features, including:

Main code for BAMS

  1. identification division.
  2. program-id. BAMS.
  3.  
  4. environment division.
  5. configuration section.
  6. special-names.
  7. crt status is CommandKeys.
  8. alphabet mixed is " ZzYyXxWwVvUuTtSsRrQqPpOoNnMmLlKkJjIiHhGgFfEeDdCcBbAa".
  9. class HexNumber is "0" thru "9", "A" thru "F", "a" thru "f".
  10. repository.
  11. function all intrinsic.
  12.  
  13. input-output section.
  14. file-control.
  15. select optional AttendeesFile assign to AttendeesFileName
  16. organization is indexed
  17. access mode is dynamic
  18. record key is AuthCode of AttendeeRecord
  19. file status is DataFileStatus.
  20.  
  21. select optional BackupFile assign to BackupFileName
  22. organization is line sequential.
  23.  
  24. data division.
  25. file section.
  26. fd AttendeesFile.
  27. copy DD-Attendee replacing Attendee by
  28. ==AttendeeRecord.
  29. 88 EndOfAttendeesFile value high-values==.
  30.  
  31. fd BackupFile.
  32. copy DD-Attendee replacing Attendee by
  33. ==BackupRecord.
  34. 88 EndOfBackupFile value high-values==.
  35.  
  36. working-storage section.
  37. 01 AddAttendeeFlag pic 9 value 0.
  38. 88 AddAttendeeFlagOn value 1 when set to false is 0.
  39.  
  40. 01 AttendeesFileName pic x(20).
  41. 01 BackupFileName pic x(20).
  42.  
  43. 01 DataFileStatus pic x(2).
  44. 88 Successful value "00".
  45. 88 RecordExists value "22".
  46. 88 NoSuchRecord value "23".
  47.  
  48. 01 AttendeesTable.
  49. copy DD-Attendee replacing 01 by 02 Attendee by
  50. ==Attendee occurs 1 to 200 times
  51. depending on NumberOfAttendees
  52. ascending key is Name
  53. ascending key is Email
  54. ascending key is AuthCode
  55. indexed by AttendeeIndex==.
  56.  
  57. 01 AuthCodeToSearchFor pic x(6) value all "0".
  58.  
  59. 01 BarnCampStats.
  60. 02 PeopleOnSite pic 999 value zero.
  61. 02 PeopleSignedUp pic 999 value zero.
  62. 02 PeopleStayingTillMonday pic 999 value zero.
  63. 02 PeopleToArrive pic 999 value zero.
  64. 02 PeopleToArriveToday pic 999 value zero.
  65. 02 PeopleToBeOnSiteToday pic 999 value zero.
  66. 02 KidsOnSite pic 99 value zero.
  67. 02 KidsToArrive pic 99 value zero.
  68. 02 KidsToArriveToday pic 99 value zero.
  69. 02 TotalEstimatedAttendees pic 999 value zero.
  70. 02 TotalEstimatedKids pic 99 value zero.
  71.  
  72. 01 EmailToSearchFor pic x(40) value spaces.
  73.  
  74. 01 EventTable.
  75. 02 EventFileName pic x(20) value "attendees.dat".
  76. 02 EventName pic x(30) value "BarnCamp".
  77. 02 EventNamePosition pic 99 value 8.
  78.  
  79. 01 NameToSearchFor pic x(25).
  80. 01 NumberOfAttendees pic 999.
  81.  
  82. 01 Command pic x.
  83. copy DD-CommandKeys.
  84.  
  85. 01 CommandLineArgumentCount pic 9 value zero.
  86.  
  87. copy DD-Attendee replacing Attendee by
  88. ==CurrentAttendee==.
  89.  
  90. 01 CurrentAttendeeNumber pic 999 value zero.
  91. 01 CurrentRow pic 99 value zero.
  92.  
  93. 01 CurrentDayOfWeek pic 9 value zero.
  94. 01 DaysOfTheWeek value "MonTueWedThuFriSatSun".
  95. 02 DayOfTheWeek pic xxx occurs 7 times.
  96. 88 ValidDayOfWeek values "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun".
  97.  
  98. 01 DefaultAmountToPay constant as 50.
  99.  
  100. 01 FirstRecordToShow pic 999 value 1.
  101.  
  102. 01 ColourScheme.
  103. 88 ColourSchemeIsMonochrome value 02.
  104. 88 ColourSchemeIsColour value 17.
  105. 02 BackgroundColour pic 9 value 0.
  106. 02 ForegroundColour pic 9 value 2.
  107.  
  108. 01 LastRecordToShow pic 999 value 20.
  109. 01 PageOffset pic 999 value 1.
  110. 01 RecordsPerPage constant as 20.
  111. 01 RecordSelected pic 999.
  112.  
  113. 01 RecordStatus pic 9 value 0.
  114. 88 RecordFound value 1 when set to false is 0.
  115.  
  116. copy DD-ScreenHeader.
  117.  
  118. screen section.
  119. 01 HomeScreen background-color BackgroundColour foreground-color ForegroundColour.
  120. 03 blank screen.
  121. 03 line 1 column 1 from ScreenHeader reverse-video.
  122. 03 line 5 column EventNamePosition from EventName.
  123. 03 line 7 column 34 value "Today is ".
  124. 03 line 7 column plus 1 from DayOfTheWeek(CurrentDayOfWeek).
  125. 03 line 10 column 5 value "Adults on site: ".
  126. 03 pic zzz9 line 10 column plus 3 from PeopleOnSite.
  127. 03 line 11 column 5 value "Adults to arrive: ".
  128. 03 pic zzz9 line 11 column plus 1 from PeopleToArrive.
  129. 03 line 12 column 5 value " " underline.
  130. 03 line 13 column 5 value "Total adults: ".
  131. 03 pic zzz9 line 13 column plus 2 from TotalEstimatedAttendees.
  132. 03 line 16 column 5 value "To arrive today: ".
  133. 03 pic zzz9 line 16 column plus 2 from PeopleToArriveToday.
  134. 03 line 17 column 5 value "To be onsite today: ".
  135. 03 pic zzz9 line 17 column minus 1 from PeopleToBeOnSiteToday.
  136. 03 line 19 column 5 value "Staying till Mon: ".
  137. 03 pic zzz9 line 19 column plus 1 from PeopleStayingTillMonday.
  138. 03 line 10 column 50 value "Kids on-site: ".
  139. 03 pic z9 line 10 column plus 5 from KidsOnSite.
  140. 03 line 11 column 50 value "Kids to arrive: ".
  141. 03 pic z9 line 11 column plus 3 from KidsToArrive.
  142. 03 line 12 column 50 value " " underline.
  143. 03 line 13 column 50 value "Total kids:".
  144. 03 pic z9 line 13 column plus 8 from TotalEstimatedKids.
  145. 03 line 16 column 45 value "Kids to arrive today: ".
  146. 03 pic z9 line 16 column plus 2 from KidsToArriveToday.
  147. 03 line 24 column 1
  148. value "Commands: F2 List, F3 Add, F4 Edit, F9 Mono/Colour, F10 Exit " reverse-video.
  149. 03 line 24 column 78 to Command.
  150.  
  151. 01 EditScreen background-color BackgroundColour foreground-color ForegroundColour.
  152. 03 blank screen.
  153. 03 line 1 column 1 from ScreenHeader reverse-video.
  154. 03 line 2 column 1 value "AuthCode:".
  155. 03 line 2 column 15 from AuthCode of CurrentAttendee.
  156. 03 line 2 column 76 value "#".
  157. 03 line 2 column plus 1 from CurrentAttendeeNumber.
  158. 03 line 4 column 1 value "Name:".
  159. 03 line 4 column 15 using Name of CurrentAttendee required.
  160. 03 line 6 column 1 value "Email:".
  161. 03 line 6 column 15 using Email of CurrentAttendee.
  162. 03 line 8 column 1 value "Telephone:".
  163. 03 line 8 column 15 using Telephone of CurrentAttendee.
  164. 03 line 10 column 1 value "Arrival day:".
  165. 03 line 10 column 15 from ArrivalDay of CurrentAttendee.
  166. 03 line 10 column plus 2 value "(Wed/Thu/Fri/Sat)".
  167. 03 line 12 column 1 value "Status:".
  168. 03 line 12 column 15 from AttendanceStatus of CurrentAttendee.
  169. 03 line 12 column plus 2 value "(A = arrived, C = coming, X = cancelled)".
  170. 03 line 14 column 1 value "Kids:".
  171. 03 pic 9 line 14 column 15 using NumberOfKids of CurrentAttendee required.
  172. 03 line 16 column 1 value "Pay amount:".
  173. 03 pic 999 line 16 column 15 using AmountToPay of CurrentAttendee required full.
  174. 03 line 18 column 1 value "Paid?:".
  175. 03 line 18 column 15 from PaymentStatus of CurrentAttendee.
  176. 03 line 20 column 1 value "Diet issues:".
  177. 03 line 20 column 15 using Diet of CurrentAttendee.
  178. 03 line 24 column 1 value "Commands: F1 Home; Toggle: F5 Arrival, F6 Status, F7 Paid; F8 Save " reverse-video.
  179. 03 line 24 column 78 to Command.
  180.  
  181. 01 ListScreen background-color BackgroundColour foreground-color ForegroundColour.
  182. 03 blank screen.
  183. 03 line 1 column 1 from ScreenHeader reverse-video.
  184. 03 line 2 column 1 value "Num" underline.
  185. 03 line 2 column 6 value "Name" underline.
  186. 03 line 2 column 31 value "Email" underline.
  187. 03 line 2 column 71 value "AuthCode" underline.
  188. 03 line 24 column 1 value "Commands: F1 Home, PgUp/PgDown to scroll, Enter number and press ENTER " reverse-video.
  189.  
  190. 01 SearchScreen background-color BackgroundColour foreground-color ForegroundColour.
  191. 03 blank screen.
  192. 03 line 1 column 1 from ScreenHeader reverse-video.
  193. 03 line 2 column 1 value "Enter AuthCode, Name, or Email and search - F2 to list all attendees:".
  194. 03 line 4 column 1 value "AuthCode: ".
  195. 03 line 4 column plus 2 to AuthCodeToSearchFor.
  196. 03 line 6 column 1 value "Name: ".
  197. 03 line 6 column plus 2 to NameToSearchFor.
  198. 03 line 8 column 1 value "Email: ".
  199. 03 line 8 column plus 2 to EmailToSearchFor.
  200. 03 line 24 column 1
  201. value "Commands: F1 Home, F2 List; Search: F5 AuthCode, F6 Name, F7 Email " reverse-video.
  202.  
  203. 01 ViewScreen background-color BackgroundColour foreground-color ForegroundColour.
  204. 03 blank screen.
  205. 03 line 1 column 1 from ScreenHeader reverse-video.
  206. 03 line 2 column 1 value "AuthCode:".
  207. 03 line 2 column 15 from AuthCode of CurrentAttendee.
  208. 03 line 4 column 1 value "Name:".
  209. 03 line 4 column 15 from Name of CurrentAttendee.
  210. 03 line 6 column 1 value "Email:".
  211. 03 line 6 column 15 from Email of CurrentAttendee.
  212. 03 line 8 column 1 value "Telephone:".
  213. 03 line 8 column 15 from Telephone of CurrentAttendee.
  214. 03 line 10 column 1 value "Arrival day:".
  215. 03 line 10 column 15 from ArrivalDay of CurrentAttendee.
  216. 03 line 12 column 1 value "Status:".
  217. 03 line 12 column 15 from AttendanceStatus of CurrentAttendee.
  218. 03 line 14 column 1 value "Kids:".
  219. 03 line 14 column 15 from NumberOfKids of CurrentAttendee.
  220. 03 line 16 column 1 value "Pay amount:".
  221. 03 pic 999 line 16 column 15 from AmountToPay of CurrentAttendee.
  222. 03 line 18 column 1 value "Paid?:".
  223. 03 line 18 column 15 from PaymentStatus of CurrentAttendee.
  224. 03 line 20 column 1 value "Diet issues:".
  225. 03 line 20 column 15 from Diet of CurrentAttendee.
  226. 03 line 24 column 1
  227. value "Commands: F1 Home, F4 Edit " reverse-video.
  228. 03 line 24 column 78 to Command.
  229.  
  230. procedure division.
  231.  
  232. Main section.
  233. perform EnableExtendedKeyInput
  234. perform LoadEventDetails
  235. perform SetupAttendeesDataFileName
  236. perform LoadDataFileIntoTable
  237. set ColourSchemeIsColour to true
  238.  
  239. perform until CommandKeyIsF10
  240. perform DisplayHomeScreen
  241. end-perform
  242.  
  243. stop run
  244. .
  245.  
  246. EnableExtendedKeyInput section.
  247. set environment 'COB_SCREEN_EXCEPTIONS' to 'Y'
  248. set environment 'COB_SCREEN_ESC' to 'Y'
  249. .
  250.  
  251. LoadEventDetails section.
  252. compute EventNamePosition = 40 - (length(trim(EventName)) / 2)
  253. .
  254.  
  255. SetupAttendeesDataFileName section.
  256. accept CommandLineArgumentCount from argument-number
  257. if CommandLineArgumentCount equal to 1 then
  258. accept AttendeesFileName from argument-value
  259. else
  260. move EventFileName to AttendeesFileName
  261. end-if
  262. .
  263.  
  264. LoadDataFileIntoTable section.
  265. move zeroes to AuthCode of AttendeeRecord
  266. start AttendeesFile key is greater than AuthCode of AttendeeRecord
  267. open input AttendeesFile
  268. read AttendeesFile next record
  269. at end set EndOfAttendeesFile to true
  270. end-read
  271. if not EndOfAttendeesFile then
  272. perform with test before varying NumberOfAttendees from 1 by 1 until EndOfAttendeesFile
  273. move AttendeeRecord to Attendee(NumberOfAttendees)
  274. read AttendeesFile next record
  275. at end set EndOfAttendeesFile to true
  276. end-read
  277. end-perform
  278. end-if
  279. close AttendeesFile
  280.  
  281. sort Attendee
  282. on descending key Name of Attendee
  283. collating sequence is mixed
  284. .
  285.  
  286. DisplayHomeScreen section.
  287. perform SetupHomeScreenStats
  288. accept HomeScreen from crt end-accept
  289. evaluate true
  290. when CommandKeyIsF2
  291. perform ListAttendees
  292. perform EditAttendee
  293. when CommandKeyIsF3 perform AddAttendee
  294. when CommandKeyIsF4 perform SearchAttendees
  295. when CommandKeyIsF9
  296. if ColourSchemeIsMonochrome then
  297. set ColourSchemeIsColour to true
  298. else
  299. set ColourSchemeIsMonochrome to true
  300. end-if
  301. end-evaluate
  302. .
  303.  
  304. SetupHomeScreenStats section.
  305. accept CurrentDayOfWeek from day-of-week
  306. initialize PeopleSignedUp, PeopleOnSite, PeopleToArrive, PeopleToArriveToday,
  307. KidsOnSite, KidsToArrive, KidsToArriveToday
  308. perform varying CurrentAttendeeNumber from 1 by 1
  309. until CurrentAttendeeNumber equal to NumberOfAttendees
  310. evaluate true
  311. when AttendeeArrived of Attendee(CurrentAttendeeNumber)
  312. add 1 to PeopleOnSite
  313. add NumberOfKids of Attendee(CurrentAttendeeNumber) to KidsOnSite
  314. if CanStayTillMonday of Attendee(CurrentAttendeeNumber) then
  315. add 1 to PeopleStayingTillMonday
  316. end-if
  317. when AttendeeComing of Attendee(CurrentAttendeeNumber)
  318. add 1 to PeopleToArrive
  319. add NumberOfKids of Attendee(CurrentAttendeeNumber) to KidsToArrive
  320. if ValidDayOfWeek(CurrentDayOfWeek) and
  321. ArrivalDay of Attendee(CurrentAttendeeNumber) is greater than or equal to DayOfTheWeek(CurrentDayOfWeek) then
  322. add 1 to PeopleToArriveToday
  323. add NumberOfKids of Attendee(CurrentAttendeeNumber) to KidsToArriveToday
  324. end-if
  325. end-evaluate
  326. add 1 to PeopleSignedUp
  327. add PeopleToArriveToday to PeopleOnSite giving PeopleToBeOnSiteToday
  328. end-perform
  329.  
  330. add PeopleToArrive to PeopleOnSite giving TotalEstimatedAttendees
  331. add KidsToArrive to KidsOnSite giving TotalEstimatedKids
  332. .
  333.  
  334. ListAttendees section.
  335. sort Attendee
  336. on descending key Name of Attendee
  337. collating sequence is mixed
  338.  
  339. move zero to PageOffset
  340. perform until CommandKeyIsF1 or CommandKeyIsEnter
  341. display ListScreen
  342. add 1 to PageOffset giving FirstRecordToShow
  343. move 3 to CurrentRow
  344. add PageOffset to RecordsPerPage giving LastRecordToShow
  345. perform varying CurrentAttendeeNumber from FirstRecordToShow by 1
  346. until CurrentAttendeeNumber greater than LastRecordToShow or
  347. CurrentAttendeeNumber equal to NumberOfAttendees
  348. display CurrentAttendeeNumber
  349. at line CurrentRow
  350. background-color BackgroundColour
  351. foreground-color ForegroundColour
  352. end-display
  353. display Name of Attendee(CurrentAttendeeNumber)
  354. at line CurrentRow
  355. column 6
  356. background-color BackgroundColour
  357. foreground-color ForegroundColour
  358. end-display
  359. display Email of Attendee(CurrentAttendeeNumber)
  360. at line CurrentRow
  361. column 31
  362. background-color BackgroundColour
  363. foreground-color ForegroundColour
  364. end-display
  365. display AuthCode of Attendee(CurrentAttendeeNumber)
  366. at line CurrentRow
  367. column 71
  368. background-color BackgroundColour
  369. foreground-color ForegroundColour
  370. end-display
  371. display AttendanceStatus of Attendee(CurrentAttendeeNumber)
  372. at line CurrentRow
  373. column 80
  374. background-color BackgroundColour
  375. foreground-color ForegroundColour
  376. end-display
  377. add 1 to CurrentRow
  378. end-perform
  379. accept RecordSelected at line 24 column 78 foreground-color ForegroundColour
  380. evaluate true also true
  381. when CommandKeyIsPgDn also LastRecordToShow is less than NumberOfAttendees
  382. add RecordsPerPage to PageOffset
  383. when CommandKeyIsPgUp also PageOffset is greater than or equal to RecordsPerPage
  384. subtract RecordsPerPage from PageOffset
  385. end-evaluate
  386. end-perform
  387.  
  388. if CommandKeyIsEnter
  389. and RecordSelected greater than zero
  390. and RecordSelected is less than or equal to NumberOfAttendees then
  391. move Attendee(RecordSelected) to CurrentAttendee
  392. move RecordSelected to CurrentAttendeeNumber
  393. set RecordFound to true
  394. set AddAttendeeFlagOn to false
  395. else
  396. set RecordFound to false
  397. end-if
  398. .
  399.  
  400. EditAttendee section.
  401. if not RecordFound then
  402. exit section
  403. end-if
  404.  
  405. perform until CommandKeyIsF1 or CommandKeyIsF8
  406. accept EditScreen from crt end-accept
  407. evaluate true
  408. when CommandKeyIsF8
  409. perform SaveAttendee
  410. perform ViewAttendee
  411. when CommandKeyIsF7
  412. evaluate true
  413. when AttendeePaid of CurrentAttendee set AttendeeNotPaid of CurrentAttendee to true
  414. when AttendeeNotPaid of CurrentAttendee set AttendeePaid of CurrentAttendee to true
  415. end-evaluate
  416. when CommandKeyIsF5
  417. evaluate true
  418. when ArrivalDayIsWednesday of CurrentAttendee set ArrivalDayIsThursday of CurrentAttendee to true
  419. when ArrivalDayIsThursday of CurrentAttendee set ArrivalDayIsFriday of CurrentAttendee to true
  420. when ArrivalDayIsFriday of CurrentAttendee set ArrivalDayIsSaturday of CurrentAttendee to true
  421. when ArrivalDayIsSaturday of CurrentAttendee set ArrivalDayIsWednesday of CurrentAttendee to true
  422. end-evaluate
  423. when CommandKeyIsF6
  424. evaluate true
  425. when AttendeeComing of CurrentAttendee set AttendeeArrived of CurrentAttendee to true
  426. when AttendeeArrived of CurrentAttendee set AttendeeCancelled of CurrentAttendee to true
  427. when AttendeeCancelled of CurrentAttendee set AttendeeComing of CurrentAttendee to true
  428. end-evaluate
  429. end-evaluate
  430. end-perform
  431. .
  432.  
  433. SaveAttendee section.
  434. perform CreateTimeStampedBackupFile
  435. open i-o AttendeesFile
  436. evaluate true
  437. when AddAttendeeFlagOn
  438. add 1 to CurrentAttendeeNumber
  439. set NumberOfAttendees to CurrentAttendeeNumber
  440. move CurrentAttendee to Attendee(CurrentAttendeeNumber)
  441. write AttendeeRecord from Attendee(CurrentAttendeeNumber)
  442. when not AddAttendeeFlagOn
  443. move CurrentAttendee to Attendee(CurrentAttendeeNumber)
  444. rewrite AttendeeRecord from Attendee(CurrentAttendeeNumber)
  445. end-evaluate
  446. close AttendeesFile
  447. .
  448.  
  449. CreateTimeStampedBackupFile section.
  450. move concatenate(formatted-current-date("YYYYMMDDThhmmss"), ".bak") to BackupFileName
  451. open output BackupFile
  452. perform varying CurrentRow from 1 by 1
  453. until CurrentRow equal to NumberOfAttendees
  454. move Attendee(CurrentRow) to BackupRecord
  455. write BackupRecord
  456. end-perform
  457. close BackupFile
  458. .
  459.  
  460. ViewAttendee section.
  461. perform until CommandKeyIsF1
  462. accept ViewScreen end-accept
  463. evaluate true
  464. when CommandKeyIsF4 perform EditAttendee
  465. end-evaluate
  466. end-perform
  467. .
  468.  
  469. AddAttendee section.
  470. initialize CurrentAttendee
  471. call "createAuthCode" using by reference AuthCode of CurrentAttendee
  472. move DayOfTheWeek(CurrentDayOfWeek) to ArrivalDay of CurrentAttendee
  473. set AttendeeArrived of CurrentAttendee to true
  474. set AttendeeNotPaid of CurrentAttendee to true
  475. move DefaultAmountToPay to AmountToPay of CurrentAttendee
  476. set AddAttendeeFlagOn to true
  477. set RecordFound to true
  478. perform EditAttendee
  479. .
  480.  
  481. SearchAttendees section.
  482. initialize CurrentAttendee
  483. set RecordFound to false
  484. perform until CommandKeyIsF1 or CommandKeyIsF2 or CommandKeyIsF5
  485. or CommandKeyIsF6 or CommandKeyIsF7
  486. accept SearchScreen from crt end-accept
  487. evaluate true
  488. when CommandKeyIsF2 perform ListAttendees
  489. when CommandKeyIsF5 perform SearchByAuthCode
  490. when CommandKeyIsF6 perform SearchByName
  491. when CommandKeyIsF7 perform SearchByEmail
  492. end-evaluate
  493. end-perform
  494.  
  495. if RecordFound then
  496. perform EditAttendee
  497. end-if
  498. .
  499.  
  500. SearchByAuthCode section.
  501. if AuthCodeToSearchFor is not HexNumber then
  502. exit section
  503. end-if
  504.  
  505. search Attendee
  506. when upper-case(AuthCode of Attendee(AttendeeIndex)) is equal to upper-case(AuthCodeToSearchFor)
  507. perform SetCurrentAttendeeToFound
  508. end-search
  509. .
  510.  
  511. SearchByName section.
  512. search Attendee
  513. when upper-case(Name of Attendee(AttendeeIndex)) is equal to upper-case(NameToSearchFor)
  514. perform SetCurrentAttendeeToFound
  515. end-search
  516. .
  517.  
  518. SearchByEmail section.
  519. search Attendee
  520. when upper-case(Email of Attendee(AttendeeIndex)) is equal to upper-case(EmailToSearchFor)
  521. perform SetCurrentAttendeeToFound
  522. end-search
  523. .
  524.  
  525. SetCurrentAttendeeToFound section.
  526. set CurrentAttendeeNumber to AttendeeIndex
  527. move Attendee(CurrentAttendeeNumber) to CurrentAttendee
  528. set RecordFound to true
  529. .
  530.  
  531. end program BAMS.
  532.  

For the rest of the code listings, refer to the GitHub project page for BAMS.

Resources

(c) 2017 Mike Harris. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".