haas/fall2026/unix/projects/pwn0.md
... ...
@@ -2,6 +2,8 @@
2 2
3 3
# PROJECT: PROCESSING WARRANTED NUMBERS (pwn0)
4 4
5
+**Part 1 of 2** (the script is completed in **pwn1**)
6
+
5 7
## TOOLBOX
6 8
7 9
In addition to the tools you're already familiar with, it is recommended
... ...
@@ -19,6 +21,10 @@ definitely offer up functionality that some solutions can make use of):
19 21
* **sort(1)**
20 22
* **uniq(1)**
21 23
24
+Don't forget the manual pages for **bash(1)** itself (see the sections on
25
+arithmetic evaluation, parameter expansion, and conditional expressions).
26
+Reading manual pages to discover capabilities is part of the exercise.
27
+
22 28
## REFERENCE
23 29
24 30
For additional information on the **printf(1)** tool recommended for use
... ...
@@ -32,9 +38,20 @@ likely portable to the UNIX variant of the tool.
32 38
33 39
## OBJECTIVE
34 40
35
-To create a script that can, with or without information provided by the
41
+To create a script that can, with or without information provided by the
36 42
user, display useful reference data in various number bases.
37 43
44
+This is the first of two projects that build one script:
45
+
46
+ * **pwn0** (this project)- the foundation: a basic table of binary, octal,
47
+ decimal, and hexadecimal values, a starting and ending value (counting
48
+ forward or backward), bounds checking, and online help.
49
+ * **pwn1**- completes the script: skipping values (**by#**), adding and
50
+ removing bases (**with#**/**sans#**), and hardening the argument handling.
51
+
52
+Do **not** implement the pwn1 features here. In pwn0, those arguments are
53
+simply unrecognized, and are to be silently ignored (see the argument rules).
54
+
38 55
## BACKGROUND
39 56
40 57
We've encountered many things in our UNIX journey so far, and have been
... ...
@@ -105,16 +122,11 @@ lab46:~/src/SEMESTER/unix/pwn0$
105 122
Your script should also take the following optional arguments (in this
106 123
order):
107 124
108
- * **$1**- starting value (some value greater than or equal to 0, and less than 256)
109
- * **$2**- ending value (some value greater than or equal to 0, and less than 256)
110
-
111
-Numerical arguments are to be given in decimal.
125
+ * **$1**- starting value (a whole number, in decimal)
126
+ * **$2**- ending value (a whole number, in decimal)
112 127
113
-Additional arguments your script should support:
128
+Additional argument your script should support:
114 129
115
- * **by#** - iterate through values by # (default: 1)
116
- * **with#** - include base # in the list of bases to display
117
- * **sans#** - remove base # in the list of bases to display
118 130
* **help** - display script usage and exit
119 131
120 132
With any of these arguments validly provided, they should adjust the
... ...
@@ -124,11 +136,13 @@ Some additional constraints/assumptions you can make:
124 136
125 137
* the minimum lower limit your script should process for is **0**. If an input goes negative, cap it to 0.
126 138
* the maximum upper limit you can implement for is **255** (base 10). If an input goes in excess of this, cap it to 255.
139
+ * the bases displayed are always 2, 8, 10, and 16 (in that order)
127 140
128 141
### ONLY SPECIFYING STARTING VALUE
129 142
130 143
In the event only the starting value is specified, assume a count of 16
131
-(so display 16 values, starting at the starting value).
144
+(so display 16 values, starting at the starting value; that is, an ending
145
+value of the starting value + 15, capped at 255).
132 146
133 147
For example:
134 148
... ...
@@ -203,62 +217,6 @@ lab46:~/src/SEMESTER/unix/pwn0$ ./pwn0.sh 73 59
203 217
lab46:~/src/SEMESTER/unix/pwn0$
204 218
```
205 219
206
-### SKIPPING VALUES
207
-
208
-With the 'by#' argument, we can cause numbers to iterate by more than 1.
209
-For example, if we wanted to go from 3 to 81 skipping 3 values each turn:
210
-
211
-```
212
-lab46:~/src/SEMESTER/unix/pwn0$ ./pwn0.sh 3 81 by3
213
- base 2 | base 8 | base10 | base16 |
214
-----------+----------+----------+----------+
215
- 11 | 3 | 3 | 3 |
216
- 110 | 6 | 6 | 6 |
217
- 1001 | 11 | 9 | 9 |
218
- 1100 | 14 | 12 | C |
219
- 1111 | 17 | 15 | F |
220
- 10010 | 22 | 18 | 12 |
221
- 10101 | 25 | 21 | 15 |
222
- 11000 | 30 | 24 | 18 |
223
- 11011 | 33 | 27 | 1B |
224
- 11110 | 36 | 30 | 1E |
225
- 100001 | 41 | 33 | 21 |
226
- 100100 | 44 | 36 | 24 |
227
- 100111 | 47 | 39 | 27 |
228
- 101010 | 52 | 42 | 2A |
229
- 101101 | 55 | 45 | 2D |
230
- 110000 | 60 | 48 | 30 |
231
- 110011 | 63 | 51 | 33 |
232
- 110110 | 66 | 54 | 36 |
233
- 111001 | 71 | 57 | 39 |
234
- 111100 | 74 | 60 | 3C |
235
- 111111 | 77 | 63 | 3F |
236
- 1000010 | 102 | 66 | 42 |
237
- 1000101 | 105 | 69 | 45 |
238
- 1001000 | 110 | 72 | 48 |
239
- 1001011 | 113 | 75 | 4B |
240
- 1001110 | 116 | 78 | 4E |
241
- 1010001 | 121 | 81 | 51 |
242
-lab46:~/src/SEMESTER/unix/pwn0$
243
-```
244
-
245
-This should be flexible whether going forward or backward in count:
246
-
247
-```
248
-lab46:~/src/SEMESTER/unix/pwn0$ ./pwn0.sh 32 2 by4
249
- base 2 | base 8 | base10 | base16 |
250
-----------+----------+----------+----------+
251
- 100000 | 40 | 32 | 20 |
252
- 11100 | 34 | 28 | 1C |
253
- 11000 | 30 | 24 | 18 |
254
- 10100 | 24 | 20 | 14 |
255
- 10000 | 20 | 16 | 10 |
256
- 1100 | 14 | 12 | C |
257
- 1000 | 10 | 8 | 8 |
258
- 100 | 4 | 4 | 4 |
259
-lab46:~/src/SEMESTER/unix/pwn0$
260
-```
261
-
262 220
### EXCEEDING BOUNDS
263 221
264 222
Although our script does not need to iterate values outside the 0-255
... ...
@@ -297,186 +255,77 @@ lab46:~/src/SEMESTER/unix/pwn0$
297 255
Both starting and ending values need to appropriately fall within the
298 256
0-255 bounds, however they may be situated.
299 257
300
-### ADDITIONAL BASES
301
-
302
-Your script should also support the inclusion of additional bases (valid
303
-bases are any base between 2 and 16, inclusive). The default bases
304
-assumed included are 2, 8, 10, and 16, but additional ones may be
305
-specified with a **with#** argument.
306
-
307
-For instance, let's say we wanted to include base 7:
308
-
309
-```
310
-lab46:~/src/SEMESTER/unix/pwn0$ ./pwn0.sh with7
311
- base 2 | base 7 | base 8 | base10 | base16 |
312
-----------+----------+----------+----------+----------+
313
- 0 | 0 | 0 | 0 | 0 |
314
- 1 | 1 | 1 | 1 | 1 |
315
- 10 | 2 | 2 | 2 | 2 |
316
- 11 | 3 | 3 | 3 | 3 |
317
- 100 | 4 | 4 | 4 | 4 |
318
- 101 | 5 | 5 | 5 | 5 |
319
- 110 | 6 | 6 | 6 | 6 |
320
- 111 | 10 | 7 | 7 | 7 |
321
- 1000 | 11 | 10 | 8 | 8 |
322
- 1001 | 12 | 11 | 9 | 9 |
323
- 1010 | 13 | 12 | 10 | A |
324
- 1011 | 14 | 13 | 11 | B |
325
- 1100 | 15 | 14 | 12 | C |
326
- 1101 | 16 | 15 | 13 | D |
327
- 1110 | 20 | 16 | 14 | E |
328
- 1111 | 21 | 17 | 15 | F |
329
-lab46:~/src/SEMESTER/unix/pwn0$
330
-```
331
-
332
-And this should work even with the potential inclusion of starting,
333
-ending, or a by# option (forward or backward):
334
-
335
-```
336
-lab46:~/src/SEMESTER/unix/pwn0$ ./pwn0.sh 13 255 by13 with7
337
- base 2 | base 7 | base 8 | base10 | base16 |
338
-----------+----------+----------+----------+----------+
339
- 1101 | 16 | 15 | 13 | D |
340
- 11010 | 35 | 32 | 26 | 1A |
341
- 100111 | 54 | 47 | 39 | 27 |
342
- 110100 | 103 | 64 | 52 | 34 |
343
- 1000001 | 122 | 101 | 65 | 41 |
344
- 1001110 | 141 | 116 | 78 | 4E |
345
- 1011011 | 160 | 133 | 91 | 5B |
346
- 1101000 | 206 | 150 | 104 | 68 |
347
- 1110101 | 225 | 165 | 117 | 75 |
348
- 10000010 | 244 | 202 | 130 | 82 |
349
- 10001111 | 263 | 217 | 143 | 8F |
350
- 10011100 | 312 | 234 | 156 | 9C |
351
- 10101001 | 331 | 251 | 169 | A9 |
352
- 10110110 | 350 | 266 | 182 | B6 |
353
- 11000011 | 366 | 303 | 195 | C3 |
354
- 11010000 | 415 | 320 | 208 | D0 |
355
- 11011101 | 434 | 335 | 221 | DD |
356
- 11101010 | 453 | 352 | 234 | EA |
357
- 11110111 | 502 | 367 | 247 | F7 |
358
-lab46:~/src/SEMESTER/unix/pwn0$
359
-```
360
-
361
-Multiple additional bases can be included... you merely append additional
362
-**with#** arguments:
363
-
364
-```
365
-lab46:~/src/SEMESTER/unix/pwn0$ ./pwn0.sh 196 2 with3 by14 with12
366
- base 2 | base 3 | base 8 | base10 | base12 | base16 |
367
-----------+----------+----------+----------+----------+----------+
368
- 11000100 | 21021 | 304 | 196 | 144 | C4 |
369
- 10110110 | 20202 | 266 | 182 | 132 | B6 |
370
- 10101000 | 20020 | 250 | 168 | 120 | A8 |
371
- 10011010 | 12201 | 232 | 154 | 10A | 9A |
372
- 10001100 | 12012 | 214 | 140 | B8 | 8C |
373
- 1111110 | 11200 | 176 | 126 | A6 | 7E |
374
- 1110000 | 11011 | 160 | 112 | 94 | 70 |
375
- 1100010 | 10122 | 142 | 98 | 82 | 62 |
376
- 1010100 | 10010 | 124 | 84 | 70 | 54 |
377
- 1000110 | 2121 | 106 | 70 | 5A | 46 |
378
- 111000 | 2002 | 70 | 56 | 48 | 38 |
379
- 101010 | 1120 | 52 | 42 | 36 | 2A |
380
- 11100 | 1001 | 34 | 28 | 24 | 1C |
381
- 1110 | 112 | 16 | 14 | 12 | E |
382
-lab46:~/src/SEMESTER/unix/pwn0$
383
-```
384
-
385
-### OMITTING BASES
386
-
387
-With the **sans#** option, we can leave out specified bases (useful for
388
-excluding any of the default 2, 8, 10, or 16).
389
-
390
-Here we will omit base 8 from a default run:
391
-
392
-```
393
-lab46:~/src/SEMESTER/unix/pwn0$ ./pwn0.sh sans8
394
- base 2 | base10 | base16 |
395
-----------+----------+----------+
396
- 0 | 0 | 0 |
397
- 1 | 1 | 1 |
398
- 10 | 2 | 2 |
399
- 11 | 3 | 3 |
400
- 100 | 4 | 4 |
401
- 101 | 5 | 5 |
402
- 110 | 6 | 6 |
403
- 111 | 7 | 7 |
404
- 1000 | 8 | 8 |
405
- 1001 | 9 | 9 |
406
- 1010 | 10 | A |
407
- 1011 | 11 | B |
408
- 1100 | 12 | C |
409
- 1101 | 13 | D |
410
- 1110 | 14 | E |
411
- 1111 | 15 | F |
412
-lab46:~/src/SEMESTER/unix/pwn0$
413
-```
414
-
415
-Of course, this option can be validly mixed with any of the other valid
416
-options, and needs to work as appropriate (here you'll see a run we did
417
-prior, with the only difference being the omission of base 8):
418
-
419
-```
420
-lab46:~/src/SEMESTER/unix/pwn0$ ./pwn0.sh 196 2 with3 by14 with12 sans8
421
- base 2 | base 3 | base10 | base12 | base16 |
422
-----------+----------+----------+----------+----------+
423
- 11000100 | 21021 | 196 | 144 | C4 |
424
- 10110110 | 20202 | 182 | 132 | B6 |
425
- 10101000 | 20020 | 168 | 120 | A8 |
426
- 10011010 | 12201 | 154 | 10A | 9A |
427
- 10001100 | 12012 | 140 | B8 | 8C |
428
- 1111110 | 11200 | 126 | A6 | 7E |
429
- 1110000 | 11011 | 112 | 94 | 70 |
430
- 1100010 | 10122 | 98 | 82 | 62 |
431
- 1010100 | 10010 | 84 | 70 | 54 |
432
- 1000110 | 2121 | 70 | 5A | 46 |
433
- 111000 | 2002 | 56 | 48 | 38 |
434
- 101010 | 1120 | 42 | 36 | 2A |
435
- 11100 | 1001 | 28 | 24 | 1C |
436
- 1110 | 112 | 14 | 12 | E |
437
-lab46:~/src/SEMESTER/unix/pwn0$
438
-```
439
-
440
-Multiple **sans#** options can be given, and will cancel out any present
441
-base (including those specified with a **with#** option). A **sans#**
442
-option for a non-existent base should be silently ignored.
443
-
444 258
### ONLINE HELP
445 259
446
-Your script should also, when prompted, display usage information with
260
+Your script should also, when prompted, display usage information with
447 261
the **help** option:
448 262
449 263
```
450 264
lab46:~/src/SEMESTER/unix/pwn0$ ./pwn0.sh help
451 265
452
- pwn0.sh - render a number table in the specified bases,
266
+ pwn0.sh - render a number table in bases 2, 8, 10, and 16,
453 267
displaying from starting value to ending value.
454 268
455
- behavior: by default, bases 2, 8, 10, and 16 will be
456
- displayed, in a range of 0 to 15 (inclusive).
269
+ behavior: by default, values 0 to 15 (inclusive) will
270
+ be displayed.
457 271
458
- note: valid bases range from 2-16, and values should
459
- not go negative nor exceed 255. If a starting
460
- value but no ending value is given, assume an
461
- ending value +15 away (or capped by 255).
272
+ note: values should not go negative nor exceed 255. If
273
+ a starting value but no ending value is given,
274
+ assume an ending value +15 away (or capped by 255).
462 275
463
- usage: pwn0.sh [start#] [end#] [OPTION]...
276
+ usage: pwn0.sh [start#] [end#]
464 277
465 278
options:
466
- $1 - if a positive decimal value, override the
279
+ $1 - if a non-negative decimal value, override the
467 280
starting value (0)
468
- $2 - if a positive decimal value, override the
281
+ $2 - if a non-negative decimal value, override the
469 282
ending value (15)
470 283
471 284
other options:
472
- by# - iterate through values by # (default: 1)
473
- with# - include base # in the list of bases to display
474
- sans# - remove base # in the list of bases to display
475 285
help - display script usage and exit
476 286
477 287
lab46:~/src/SEMESTER/unix/pwn0$
478 288
```
479 289
290
+(The online help is provided in the starter script; the point is for you to
291
+produce it correctly, and to understand how it works.)
292
+
293
+### ARGUMENT RULES (CLARIFICATIONS)
294
+
295
+To remove ambiguity, here is exactly how arguments are to be treated:
296
+
297
+ * **help** anywhere on the command line wins: display usage and exit (even
298
+ if other arguments are present).
299
+ * Arguments are examined in the order given. The first whole number
300
+ encountered is the starting value, the second is the ending value. Any
301
+ further whole numbers are silently ignored.
302
+ * A "whole number" is an optional leading `-` followed by one or more digits.
303
+ Things like `12.5`, `abc`, `+5`, or `0x1F` are not whole numbers.
304
+ * Anything that is neither **help** nor a whole number is silently dropped
305
+ (as if never given). This includes **by#**, **with#**, and **sans#**,
306
+ which you will meet in pwn1 but which mean nothing yet.
307
+ * Values below 0 become 0; values above 255 become 255 (this includes
308
+ ridiculously long numbers). This applies to the start and the end value.
309
+ * If only a starting value is given (or none at all), the ending value is
310
+ the starting value + 15, capped at 255.
311
+ * If the starting value is higher than the ending value, count backward
312
+ (one at a time). If they are equal, display a single row.
313
+ * Nothing goes to STDERR, ever: this script has no error messages. The exit
314
+ status is always 0.
315
+
316
+For example, unrecognized arguments are simply ignored:
317
+
318
+```
319
+lab46:~/src/SEMESTER/unix/pwn0$ ./pwn0.sh 5 8 by2 foo
320
+ base 2 | base 8 | base10 | base16 |
321
+----------+----------+----------+----------+
322
+ 101 | 5 | 5 | 5 |
323
+ 110 | 6 | 6 | 6 |
324
+ 111 | 7 | 7 | 7 |
325
+ 1000 | 10 | 8 | 8 |
326
+lab46:~/src/SEMESTER/unix/pwn0$
327
+```
328
+
480 329
## SPECIFICATIONS
481 330
482 331
Evaluation will be based on correctness of values as well as on
... ...
@@ -493,9 +342,9 @@ You'll notice that everything lines up and is positioned similarly:
493 342
* The dashes and vertical bars set up an ASCII art-like table pattern that helps make the data more readable.
494 343
* The values are right justified with each column
495 344
* justification is essential. Everything should line up on its respective right margin.
496
- * base headings are right justified, with a 2 spaces being allocated for reference of the base:
345
+ * base headings are right justified; the word "base" is followed by the base number in a field 2 characters wide:
497 346
* you'll see "base 2" vs. "base16"; your script needs to accommodate this.
498
- * You are to literally print out no more than 2 consecutive spaces at a time in your output statements. If you need more, use one of the formatting tools to generate spaces for you (**printf(1)** may be able to help you with this).
347
+ * You are to literally print out no more than 2 consecutive spaces at a time in your output statements (the **help** text is exempt from this rule). If you need more, use one of the formatting tools to generate spaces for you (**printf(1)** may be able to help you with this).
499 348
* You need to check your arguments to ensure they are present and valid. Some examples:
500 349
* starting value is a valid (whole) number
501 350
* if less than 0, or greater than 255, needs to be capped to whichever extreme it exceeds
... ...
@@ -505,10 +354,9 @@ You'll notice that everything lines up and is positioned similarly:
505 354
* if less than 0, or greater than 255, needs to be capped to whichever extreme it exceeds
506 355
* if -8, cap to 0
507 356
* if 420, cap to 255
508
- * any invalid arguments (nonsensical value in place of starting value, invalid base specification, etc.) should be silently dropped/ignored.
357
+ * any invalid arguments (nonsensical value in place of starting value, etc.) should be silently dropped/ignored.
509 358
* default starting value is 0, default ending value is 15. If nothing else is validly provided, these are the values the script should run with.
510 359
* if starting and ending values are the same, the script will display just one line of number output (in addition to the header).
511
- * if lacking any bases to display, silently exit
512 360
* your script needs to commence with a proper **shabang** to bash; your script needs to end with an "**exit 0**" at the very end
513 361
* comments and indentation are required and necessary
514 362
* comments should explain how or why you are doing something
... ...
@@ -528,7 +376,7 @@ You'll notice that everything lines up and is positioned similarly:
528 376
* if you gain inspiration from some external resource, please cite it
529 377
* comments are a great way of demonstrating understanding (if you explain the why and how effectively, and it isn't in violation of other aspects, I'll know you are in control of things)
530 378
531
-To be sure, I'll be checking to make sure you solution follows the spirit
379
+To be sure, I'll be checking to make sure your solution follows the spirit
532 380
of what this project is about (that you implement functional, flowing
533 381
logic utilizing the tools and concepts we've learned, in an application
534 382
that helps demonstrate your comprehension). Don't try to weasel your way
... ...
@@ -884,15 +732,102 @@ can run 0 or 1 times, a while loop can run 0 or more.
884 732
count=10
885 733
while [ "${count}" -gt 0 ]; do
886 734
echo "count is: ${count}"
735
+ count=$((count - 1))
736
+done
887 737
```
888 738
889
-=====Submission=====
739
+The condition works just like it does for an **if** (it is the
740
+arguments given to `[`(1)), but the body repeats for as long as that
741
+condition remains true. Something inside the loop **must** eventually change
742
+the outcome of the condition (above, the `count=$((count - 1))` line), or
743
+you have created an infinite loop (**Ctrl-C** will rescue you).
744
+
745
+Arithmetic in bash is performed with the `$(( ))` construct, as seen above.
746
+
747
+### FOR LOOPS
748
+
749
+When you know in advance what you want to iterate through, a **for** loop is
750
+often cleaner. A **list-based** for loop visits each item in a list:
751
+
752
+```
753
+for base in 2 8 10 16; do
754
+ echo "now working on base ${base}"
755
+done
756
+```
757
+
758
+A **numeric** (C-style) for loop manages a counter for you:
759
+
760
+```
761
+for ((i = 0; i < 5; i++)); do
762
+ echo "i is: ${i}"
763
+done
764
+```
765
+
766
+Either is fine for this project. Think about which loop best fits each
767
+repetitive task you identify (walking the arguments, walking the values,
768
+walking the bases...).
769
+
770
+## PATTERN MATCHING
771
+
772
+You'll need to decide what each command-line argument IS (the word
773
+`help`? a whole number? something nonsensical?). Rather than a long chain of
774
+string comparisons, the course objective on pattern matching asks you to
775
+describe the *shape* of what you expect with a **regular expression**.
776
+
777
+A few places where regular expressions can be applied in a bash script (check
778
+the manual pages and your notes on regular expressions, and experiment!):
779
+
780
+ * **grep(1)** / **egrep(1)** (or `grep -E`), commonly with `-q` to test a match
781
+ * the `[[ string =~ regex ]]` test (captured groups land in `BASH_REMATCH`)
782
+ * **sed(1)** substitutions to strip or transform text
783
+ * `case` statement patterns (these are shell globs, a close relative)
784
+
785
+Anchors (`^` and `$`) matter. A whole number (with an optional leading minus)
786
+could be described as `^-?[0-9]+$`:
787
+
788
+```
789
+lab46:~$ [[ "-8" =~ ^-?[0-9]+$ ]] && echo "whole number"
790
+whole number
791
+lab46:~$ [[ "42abc" =~ ^-?[0-9]+$ ]] || echo "42abc rejected"
792
+42abc rejected
793
+lab46:~$
794
+```
795
+
796
+Without the anchors, "42abc" would match too (it contains digits), and
797
+"12.5" would sneak through as well. Test your patterns against near-misses.
798
+
799
+## LOOKING AHEAD: PWN1
800
+
801
+The **pwn1** project picks up right where this one leaves off: you will
802
+extend this very script to skip values (**by#**), add bases (**with#**), and
803
+remove bases (**sans#**). Today's table is a fixed list of four bases; next
804
+time it will be a list that changes. Write your pwn0 so that:
805
+
806
+ * the work of converting and displaying ONE value lives in one place
807
+ (rather than being copy/pasted four times)
808
+ * your loops and conditions are tidy and easy to follow
809
+ * your comments make it obvious how everything fits together
810
+
811
+Future you (and your pwn1 grade) will thank you.
812
+
813
+## SUBMISSION
890 814
891 815
By successfully performing this project, you should have a fully
892 816
functioning script by the name of **pwn0.sh**, which is all you need to
893 817
submit for project completion (no steps file, as your "steps" file IS the
894 818
script you wrote).
895 819
820
+A **starter script** is available to give you a base to work from (it contains
821
+the identification block, the complete **help** text, and an outline of
822
+suggested steps):
823
+
824
+```
825
+$ cp /var/public/fall2026/UNIX/pwn0/pwn0.sh ~/src/SEMESTER/unix/pwn0/pwn0.sh
826
+```
827
+
828
+Using the starter is optional, but whatever you submit must still satisfy
829
+everything in this document.
830
+
896 831
To submit this project to me using the **submit** tool, run the following
897 832
command at your lab46 prompt:
898 833
... ...
@@ -904,36 +839,35 @@ Submitting unix project "pwn0":
904 839
SUCCESSFULLY SUBMITTED
905 840
```
906 841
907
-You should get some sort of confirmation indicating successful submission
908
-if all went according to plan. If not, check for typos and or locational
909
-mismatches.
910
-
911
-I'll be looking for the following:
842
+What I'm looking for:
912 843
913 844
```
914
-312:pwn0:final tally of results (312/312)
915
-*:pwn0:pwn0.sh on help displays informative usage and exits [18/18]
916
-*:pwn0:pwn0.sh effectively utilizes variables in operations [18/18]
917
-*:pwn0:pwn0.sh effectively utilizes command expansions [18/18]
918
-*:pwn0:pwn0.sh effectively utilizes regular expressions [18/18]
919
-*:pwn0:pwn0.sh effectively utilizes selection statements [18/18]
920
-*:pwn0:pwn0.sh effectively utilizes looping structures [18/18]
921
-*:pwn0:pwn0.sh is a proper bash script with shabang and exit [17/17]
922
-*:pwn0:pwn0.sh parses command-line arguments as appropriate [17/17]
923
-*:pwn0:pwn0.sh accurately displays values in proper alignment [17/17]
924
-*:pwn0:pwn0.sh accurately displays values in requested bases [17/17]
925
-*:pwn0:pwn0.sh accurately displays values in specified range [17/17]
926
-*:pwn0:pwn0.sh range logic flexibly works forward and reverse [17/17]
927
-*:pwn0:pwn0.sh iterates as appropriate in given scenarios [17/17]
928
-*:pwn0:pwn0.sh includes additional bases as requested [17/17]
929
-*:pwn0:pwn0.sh omits specified bases as requested [17/17]
930
-*:pwn0:pwn0.sh properly manages input violations [17/17]
931
-*:pwn0:pwn0.sh operates according to specifications [17/17]
932
-*:pwn0:pwn0.sh logic is organized and easy to read [17/17]
845
+286:pwn0:final tally of results (286/286)
846
+*:pwn0:pwn0.sh is a proper bash script: shabang, clean exit 0, parses, no hard-coded paths [13/13]
847
+*:pwn0:pwn0.sh on help displays informative usage and exits [13/13]
848
+*:pwn0:pwn0.sh formats output with printf width specifiers (no >2 literal spaces) [13/13]
849
+*:pwn0:pwn0.sh accurately displays values in proper alignment [26/26]
850
+*:pwn0:pwn0.sh effectively utilizes variables in operations [13/13]
851
+*:pwn0:pwn0.sh effectively utilizes command expansions [13/13]
852
+*:pwn0:pwn0.sh effectively utilizes selection statements [13/13]
853
+*:pwn0:pwn0.sh effectively utilizes looping structures [13/13]
854
+*:pwn0:pwn0.sh accurately displays the default table (no arguments) [26/26]
855
+*:pwn0:pwn0.sh accurately displays values for starting and ending values [26/26]
856
+*:pwn0:pwn0.sh employs discrete single-purpose utilities [13/13]
857
+*:pwn0:pwn0.sh uses regular expressions to validate numeric input [13/13]
858
+*:pwn0:pwn0.sh displays 16 values when only a starting value is given [13/13]
859
+*:pwn0:pwn0.sh range logic flexibly works forward and reverse [13/13]
860
+*:pwn0:pwn0.sh caps starting/ending values to the 0 to 255 bounds [13/13]
861
+*:pwn0:pwn0.sh silently drops invalid arguments and still runs [13/13]
862
+*:pwn0:pwn0.sh comments explain how/why (header block, density, reasoning) [13/13]
863
+*:pwn0:pwn0.sh logic is organized: consistent indentation (>=3), lines within 90 chars [13/13]
864
+*:pwn0:pwn0.sh identifies shortcomings, deviations from specification [13/13]
933 865
```
934 866
935 867
Additionally:
936
- * Solutions not abiding by **SPIRIT** of project will be subject to a 25% overall deduction
868
+
869
+ * Solutions not abiding by the **SPIRIT** of the project (including
870
+ hard-coded output) will be subject to a 25% overall deduction
937 871
* Solutions not utilizing descriptive why and how **COMMENTS** will be subject to a 25% overall deduction
938 872
* Solutions not utilizing **INDENTATION** to promote scope and clarity will be subject to a 25% overall deduction
939
- * Solutions lacking **ORGANIZATION** ior are not easy to read (within 90 char width) are subject to a 25% overall deduction
873
+ * Solutions lacking **ORGANIZATION** or that are not easy to read (within 90 char width) are subject to a 25% overall deduction
haas/fall2026/unix/projects/pwn1.md
... ...
@@ -0,0 +1,718 @@
1
+# CSCS1730 UNIX/Linux Fundamentals
2
+
3
+# PROJECT: PROCESSING WARRANTED NUMBERS (pwn1)
4
+
5
+**Part 2 of 2** (builds directly on **pwn0**)
6
+
7
+## TOOLBOX
8
+
9
+In addition to the tools you're already familiar with, it is recommended
10
+you check out the following tools for possible application in this
11
+project (you may not need to use them in your solution, but they
12
+definitely offer up functionality that some solutions can make use of):
13
+
14
+ * **bc(1)**
15
+ * **comm(1)**
16
+ * **diff(1)**
17
+ * **grep(1)**
18
+ * **join(1)**
19
+ * **printf(1)**
20
+ * **seq(1)**
21
+ * **sort(1)**
22
+ * **uniq(1)**
23
+
24
+Don't forget the manual pages for **bash(1)** itself (see the sections on
25
+arithmetic evaluation, parameter expansion, and conditional expressions).
26
+Reading manual pages to discover capabilities is part of the exercise.
27
+
28
+For this project in particular, give **bc(1)** a good look (its `obase`
29
+setting is a natural fit for converting to an arbitrary base), and consider
30
+how **sort(1)** and **uniq(1)** could help tidy up a list of bases.
31
+
32
+## REFERENCE
33
+
34
+For additional information on the **printf(1)** tool recommended for use
35
+in this project, please check out this paper:
36
+
37
+ * https://lab46.g7n.org/downloads/printf.pdf
38
+
39
+NOTE that while it focuses on the C and Perl variants of printf, the
40
+conceptual underpinnings are the same, and many of the examples are
41
+likely portable to the UNIX variant of the tool.
42
+
43
+## OBJECTIVE
44
+
45
+To complete the script begun in **pwn0**: a script that can, with or without
46
+information provided by the user, display useful reference data in various
47
+number bases, skipping through values, adding bases, removing bases, and
48
+standing up to bad input.
49
+
50
+Your finished pwn1.sh does **everything** described in this document **and**
51
+in pwn0. Begin from your own pwn0.sh. If your pwn0 had gaps, this is your
52
+chance to close them: pwn1 is evaluated against the whole script.
53
+
54
+## BACKGROUND
55
+
56
+In **pwn0** you built the foundation: a table of values in bases 2, 8, 10, and
57
+16, with a starting and ending value, forward or backward, bounds checking,
58
+and online help. The four bases were fixed, and so was the step of 1.
59
+
60
+For **pwn1**, the table becomes flexible. **Everything about it is decided at
61
+run time**:
62
+
63
+ * how far apart consecutive values are (**by#**)
64
+ * which bases appear (**with#** to add, **sans#** to remove, even the defaults)
65
+ * how gracefully the script copes with arguments that make no sense
66
+
67
+Where pwn0 had four columns written out in your logic, pwn1 has a *list of
68
+bases* that your script assembles, and that the header, the divider, and every
69
+row are built from. That shift (from "four things" to "a list of things") is the
70
+heart of this project.
71
+
72
+## PROCESS
73
+
74
+Your script does everything pwn0 did. As a reminder, with no arguments:
75
+
76
+```
77
+lab46:~/src/SEMESTER/unix/pwn1$ ./pwn1.sh
78
+ base 2 | base 8 | base10 | base16 |
79
+----------+----------+----------+----------+
80
+ 0 | 0 | 0 | 0 |
81
+ 1 | 1 | 1 | 1 |
82
+ 10 | 2 | 2 | 2 |
83
+ 11 | 3 | 3 | 3 |
84
+ 100 | 4 | 4 | 4 |
85
+ 101 | 5 | 5 | 5 |
86
+ 110 | 6 | 6 | 6 |
87
+ 111 | 7 | 7 | 7 |
88
+ 1000 | 10 | 8 | 8 |
89
+ 1001 | 11 | 9 | 9 |
90
+ 1010 | 12 | 10 | A |
91
+ 1011 | 13 | 11 | B |
92
+ 1100 | 14 | 12 | C |
93
+ 1101 | 15 | 13 | D |
94
+ 1110 | 16 | 14 | E |
95
+ 1111 | 17 | 15 | F |
96
+lab46:~/src/SEMESTER/unix/pwn1$
97
+```
98
+
99
+The full set of arguments your script should take:
100
+
101
+ * **$1**- starting value (a whole number, in decimal)
102
+ * **$2**- ending value (a whole number, in decimal)
103
+
104
+Numerical arguments are to be given in decimal.
105
+
106
+Additional arguments your script should support:
107
+
108
+ * **by#** - iterate through values by # (default: 1)
109
+ * **with#** - include base # in the list of bases to display
110
+ * **sans#** - remove base # in the list of bases to display
111
+ * **help** - display script usage and exit
112
+
113
+With any of these arguments validly provided, they should adjust the
114
+script's processing and output accordingly. Any of them may be combined with
115
+any other, in any order.
116
+
117
+Some additional constraints/assumptions you can make:
118
+
119
+ * the minimum lower limit your script should process for is **0**. If an input goes negative, cap it to 0.
120
+ * the maximum upper limit you can implement for is **255** (base 10). If an input goes in excess of this, cap it to 255.
121
+ * valid bases are 2 through 16, inclusive. The default bases are 2, 8, 10, and 16.
122
+
123
+Examples of the pwn0 behaviors (start-only, start/end, going backward, and
124
+exceeding bounds) are in the pwn0 project document; they all still apply.
125
+
126
+### SKIPPING VALUES
127
+
128
+With the 'by#' argument, we can cause numbers to iterate by more than 1.
129
+For example, if we wanted to go from 3 to 81 skipping 3 values each turn:
130
+
131
+```
132
+lab46:~/src/SEMESTER/unix/pwn1$ ./pwn1.sh 3 81 by3
133
+ base 2 | base 8 | base10 | base16 |
134
+----------+----------+----------+----------+
135
+ 11 | 3 | 3 | 3 |
136
+ 110 | 6 | 6 | 6 |
137
+ 1001 | 11 | 9 | 9 |
138
+ 1100 | 14 | 12 | C |
139
+ 1111 | 17 | 15 | F |
140
+ 10010 | 22 | 18 | 12 |
141
+ 10101 | 25 | 21 | 15 |
142
+ 11000 | 30 | 24 | 18 |
143
+ 11011 | 33 | 27 | 1B |
144
+ 11110 | 36 | 30 | 1E |
145
+ 100001 | 41 | 33 | 21 |
146
+ 100100 | 44 | 36 | 24 |
147
+ 100111 | 47 | 39 | 27 |
148
+ 101010 | 52 | 42 | 2A |
149
+ 101101 | 55 | 45 | 2D |
150
+ 110000 | 60 | 48 | 30 |
151
+ 110011 | 63 | 51 | 33 |
152
+ 110110 | 66 | 54 | 36 |
153
+ 111001 | 71 | 57 | 39 |
154
+ 111100 | 74 | 60 | 3C |
155
+ 111111 | 77 | 63 | 3F |
156
+ 1000010 | 102 | 66 | 42 |
157
+ 1000101 | 105 | 69 | 45 |
158
+ 1001000 | 110 | 72 | 48 |
159
+ 1001011 | 113 | 75 | 4B |
160
+ 1001110 | 116 | 78 | 4E |
161
+ 1010001 | 121 | 81 | 51 |
162
+lab46:~/src/SEMESTER/unix/pwn1$
163
+```
164
+
165
+This should be flexible whether going forward or backward in count:
166
+
167
+```
168
+lab46:~/src/SEMESTER/unix/pwn1$ ./pwn1.sh 32 2 by4
169
+ base 2 | base 8 | base10 | base16 |
170
+----------+----------+----------+----------+
171
+ 100000 | 40 | 32 | 20 |
172
+ 11100 | 34 | 28 | 1C |
173
+ 11000 | 30 | 24 | 18 |
174
+ 10100 | 24 | 20 | 14 |
175
+ 10000 | 20 | 16 | 10 |
176
+ 1100 | 14 | 12 | C |
177
+ 1000 | 10 | 8 | 8 |
178
+ 100 | 4 | 4 | 4 |
179
+lab46:~/src/SEMESTER/unix/pwn1$
180
+```
181
+
182
+### ADDITIONAL BASES
183
+
184
+Your script should also support the inclusion of additional bases (valid
185
+bases are any base between 2 and 16, inclusive). The default bases
186
+assumed included are 2, 8, 10, and 16, but additional ones may be
187
+specified with a **with#** argument.
188
+
189
+For instance, let's say we wanted to include base 7:
190
+
191
+```
192
+lab46:~/src/SEMESTER/unix/pwn1$ ./pwn1.sh with7
193
+ base 2 | base 7 | base 8 | base10 | base16 |
194
+----------+----------+----------+----------+----------+
195
+ 0 | 0 | 0 | 0 | 0 |
196
+ 1 | 1 | 1 | 1 | 1 |
197
+ 10 | 2 | 2 | 2 | 2 |
198
+ 11 | 3 | 3 | 3 | 3 |
199
+ 100 | 4 | 4 | 4 | 4 |
200
+ 101 | 5 | 5 | 5 | 5 |
201
+ 110 | 6 | 6 | 6 | 6 |
202
+ 111 | 10 | 7 | 7 | 7 |
203
+ 1000 | 11 | 10 | 8 | 8 |
204
+ 1001 | 12 | 11 | 9 | 9 |
205
+ 1010 | 13 | 12 | 10 | A |
206
+ 1011 | 14 | 13 | 11 | B |
207
+ 1100 | 15 | 14 | 12 | C |
208
+ 1101 | 16 | 15 | 13 | D |
209
+ 1110 | 20 | 16 | 14 | E |
210
+ 1111 | 21 | 17 | 15 | F |
211
+lab46:~/src/SEMESTER/unix/pwn1$
212
+```
213
+
214
+And this should work even with the potential inclusion of starting,
215
+ending, or a by# option (forward or backward):
216
+
217
+```
218
+lab46:~/src/SEMESTER/unix/pwn1$ ./pwn1.sh 13 255 by13 with7
219
+ base 2 | base 7 | base 8 | base10 | base16 |
220
+----------+----------+----------+----------+----------+
221
+ 1101 | 16 | 15 | 13 | D |
222
+ 11010 | 35 | 32 | 26 | 1A |
223
+ 100111 | 54 | 47 | 39 | 27 |
224
+ 110100 | 103 | 64 | 52 | 34 |
225
+ 1000001 | 122 | 101 | 65 | 41 |
226
+ 1001110 | 141 | 116 | 78 | 4E |
227
+ 1011011 | 160 | 133 | 91 | 5B |
228
+ 1101000 | 206 | 150 | 104 | 68 |
229
+ 1110101 | 225 | 165 | 117 | 75 |
230
+ 10000010 | 244 | 202 | 130 | 82 |
231
+ 10001111 | 263 | 217 | 143 | 8F |
232
+ 10011100 | 312 | 234 | 156 | 9C |
233
+ 10101001 | 331 | 251 | 169 | A9 |
234
+ 10110110 | 350 | 266 | 182 | B6 |
235
+ 11000011 | 366 | 303 | 195 | C3 |
236
+ 11010000 | 415 | 320 | 208 | D0 |
237
+ 11011101 | 434 | 335 | 221 | DD |
238
+ 11101010 | 453 | 352 | 234 | EA |
239
+ 11110111 | 502 | 367 | 247 | F7 |
240
+lab46:~/src/SEMESTER/unix/pwn1$
241
+```
242
+
243
+Multiple additional bases can be included... you merely append additional
244
+**with#** arguments:
245
+
246
+```
247
+lab46:~/src/SEMESTER/unix/pwn1$ ./pwn1.sh 196 2 with3 by14 with12
248
+ base 2 | base 3 | base 8 | base10 | base12 | base16 |
249
+----------+----------+----------+----------+----------+----------+
250
+ 11000100 | 21021 | 304 | 196 | 144 | C4 |
251
+ 10110110 | 20202 | 266 | 182 | 132 | B6 |
252
+ 10101000 | 20020 | 250 | 168 | 120 | A8 |
253
+ 10011010 | 12201 | 232 | 154 | 10A | 9A |
254
+ 10001100 | 12012 | 214 | 140 | B8 | 8C |
255
+ 1111110 | 11200 | 176 | 126 | A6 | 7E |
256
+ 1110000 | 11011 | 160 | 112 | 94 | 70 |
257
+ 1100010 | 10122 | 142 | 98 | 82 | 62 |
258
+ 1010100 | 10010 | 124 | 84 | 70 | 54 |
259
+ 1000110 | 2121 | 106 | 70 | 5A | 46 |
260
+ 111000 | 2002 | 70 | 56 | 48 | 38 |
261
+ 101010 | 1120 | 52 | 42 | 36 | 2A |
262
+ 11100 | 1001 | 34 | 28 | 24 | 1C |
263
+ 1110 | 112 | 16 | 14 | 12 | E |
264
+lab46:~/src/SEMESTER/unix/pwn1$
265
+```
266
+
267
+### OMITTING BASES
268
+
269
+With the **sans#** option, we can leave out specified bases (useful for
270
+excluding any of the default 2, 8, 10, or 16).
271
+
272
+Here we will omit base 8 from a default run:
273
+
274
+```
275
+lab46:~/src/SEMESTER/unix/pwn1$ ./pwn1.sh sans8
276
+ base 2 | base10 | base16 |
277
+----------+----------+----------+
278
+ 0 | 0 | 0 |
279
+ 1 | 1 | 1 |
280
+ 10 | 2 | 2 |
281
+ 11 | 3 | 3 |
282
+ 100 | 4 | 4 |
283
+ 101 | 5 | 5 |
284
+ 110 | 6 | 6 |
285
+ 111 | 7 | 7 |
286
+ 1000 | 8 | 8 |
287
+ 1001 | 9 | 9 |
288
+ 1010 | 10 | A |
289
+ 1011 | 11 | B |
290
+ 1100 | 12 | C |
291
+ 1101 | 13 | D |
292
+ 1110 | 14 | E |
293
+ 1111 | 15 | F |
294
+lab46:~/src/SEMESTER/unix/pwn1$
295
+```
296
+
297
+Of course, this option can be validly mixed with any of the other valid
298
+options, and needs to work as appropriate (here you'll see a run we did
299
+prior, with the only difference being the omission of base 8):
300
+
301
+```
302
+lab46:~/src/SEMESTER/unix/pwn1$ ./pwn1.sh 196 2 with3 by14 with12 sans8
303
+ base 2 | base 3 | base10 | base12 | base16 |
304
+----------+----------+----------+----------+----------+
305
+ 11000100 | 21021 | 196 | 144 | C4 |
306
+ 10110110 | 20202 | 182 | 132 | B6 |
307
+ 10101000 | 20020 | 168 | 120 | A8 |
308
+ 10011010 | 12201 | 154 | 10A | 9A |
309
+ 10001100 | 12012 | 140 | B8 | 8C |
310
+ 1111110 | 11200 | 126 | A6 | 7E |
311
+ 1110000 | 11011 | 112 | 94 | 70 |
312
+ 1100010 | 10122 | 98 | 82 | 62 |
313
+ 1010100 | 10010 | 84 | 70 | 54 |
314
+ 1000110 | 2121 | 70 | 5A | 46 |
315
+ 111000 | 2002 | 56 | 48 | 38 |
316
+ 101010 | 1120 | 42 | 36 | 2A |
317
+ 11100 | 1001 | 28 | 24 | 1C |
318
+ 1110 | 112 | 14 | 12 | E |
319
+lab46:~/src/SEMESTER/unix/pwn1$
320
+```
321
+
322
+Multiple **sans#** options can be given, and will cancel out any present
323
+base (including those specified with a **with#** option). A **sans#**
324
+option for a non-existent base should be silently ignored.
325
+
326
+### INVALID ARGUMENTS AND EMPTY TABLES
327
+
328
+Arguments that make no sense are silently dropped, as if they were never
329
+given (no error messages, and the script runs on with whatever IS valid).
330
+Here, `by0`, `with1`, `sans99`, and `foo` are all nonsense:
331
+
332
+```
333
+lab46:~/src/SEMESTER/unix/pwn1$ ./pwn1.sh 5 8 by0 with1 sans99 foo
334
+ base 2 | base 8 | base10 | base16 |
335
+----------+----------+----------+----------+
336
+ 101 | 5 | 5 | 5 |
337
+ 110 | 6 | 6 | 6 |
338
+ 111 | 7 | 7 | 7 |
339
+ 1000 | 10 | 8 | 8 |
340
+lab46:~/src/SEMESTER/unix/pwn1$
341
+```
342
+
343
+Order of the **with#** and **sans#** options does not matter: a **sans#**
344
+always wins. Below, base 3 is requested and then removed, so it never appears
345
+(and the step of 2 takes us through 5 and 7):
346
+
347
+```
348
+lab46:~/src/SEMESTER/unix/pwn1$ ./pwn1.sh 5 8 with3 by2 with3 sans3
349
+ base 2 | base 8 | base10 | base16 |
350
+----------+----------+----------+----------+
351
+ 101 | 5 | 5 | 5 |
352
+ 111 | 7 | 7 | 7 |
353
+lab46:~/src/SEMESTER/unix/pwn1$
354
+```
355
+
356
+And if every base is removed, there is nothing to display, so the script
357
+quietly exits (not even the header is printed, and the exit status is 0):
358
+
359
+```
360
+lab46:~/src/SEMESTER/unix/pwn1$ ./pwn1.sh sans2 sans8 sans10 sans16
361
+lab46:~/src/SEMESTER/unix/pwn1$
362
+```
363
+
364
+Notice the next prompt immediately follows the command: nothing was displayed.
365
+
366
+### ONLINE HELP
367
+
368
+Your script should also, when prompted, display usage information with
369
+the **help** option. The pwn1 help text covers the complete set of arguments:
370
+
371
+```
372
+lab46:~/src/SEMESTER/unix/pwn1$ ./pwn1.sh help
373
+
374
+ pwn1.sh - render a number table in the specified bases,
375
+ displaying from starting value to ending value.
376
+
377
+ behavior: by default, bases 2, 8, 10, and 16 will be
378
+ displayed, in a range of 0 to 15 (inclusive).
379
+
380
+ note: valid bases range from 2-16, and values should
381
+ not go negative nor exceed 255. If a starting
382
+ value but no ending value is given, assume an
383
+ ending value +15 away (or capped by 255).
384
+
385
+ usage: pwn1.sh [start#] [end#] [OPTION]...
386
+
387
+ options:
388
+ $1 - if a non-negative decimal value, override the
389
+ starting value (0)
390
+ $2 - if a non-negative decimal value, override the
391
+ ending value (15)
392
+
393
+ other options:
394
+ by# - iterate through values by # (default: 1)
395
+ with# - include base # in the list of bases to display
396
+ sans# - remove base # in the list of bases to display
397
+ help - display script usage and exit
398
+
399
+lab46:~/src/SEMESTER/unix/pwn1$
400
+```
401
+
402
+(The complete online help is provided in the starter script.)
403
+
404
+### ARGUMENT RULES (CLARIFICATIONS)
405
+
406
+To remove ambiguity, here is exactly how arguments are to be treated:
407
+
408
+ * **help** anywhere on the command line wins: display usage and exit (even
409
+ if other arguments are present).
410
+ * Arguments may appear in **any order** relative to one another. The first
411
+ whole number encountered is the starting value, the second is the ending
412
+ value. Any further whole numbers are silently ignored.
413
+ * A "whole number" is an optional leading `-` followed by one or more digits.
414
+ Things like `12.5`, `abc`, or `0x1F` are not whole numbers, and are
415
+ silently dropped (as if never given).
416
+ * Values below 0 become 0; values above 255 become 255 (this includes
417
+ ridiculously long numbers). This applies to the start and the end value.
418
+ * **by#** requires a whole number of at least 1. `by0`, `by`, `byx`, and
419
+ `by-3` are invalid and silently dropped. If **by#** is given more than once,
420
+ the last valid one wins. The step is always a *size*: when counting
421
+ backward, a step of 4 subtracts 4. The final value displayed is the last
422
+ one reached without passing the ending value (so `32 2 by4` ends at 4).
423
+ * **with#** and **sans#** require a base from 2 to 16 (inclusive). Anything
424
+ else (`with1`, `with17`, `withx`, `with`) is silently dropped.
425
+ * The displayed bases are: (the defaults 2, 8, 10, 16, plus any valid
426
+ **with#**) minus any **sans#**, in ascending order, with no duplicates.
427
+ A **sans#** wins regardless of where it appears (`sans3 with3` and
428
+ `with3 sans3` both result in no base 3).
429
+ * If **no bases remain**, exit quietly with a status of 0, printing nothing
430
+ at all (not even the header).
431
+ * If only a starting value is given (or none at all), the ending value is
432
+ the starting value + 15, capped at 255.
433
+ * Nothing goes to STDERR, ever: this script has no error messages. The exit
434
+ status is always 0.
435
+
436
+## SPECIFICATIONS
437
+
438
+Evaluation will be based on correctness of values as well as on
439
+formatting/spacing.
440
+
441
+You'll notice that everything lines up and is positioned similarly:
442
+
443
+ * each column exists within 10 characters of on-screen real estate:
444
+ * 8 characters for actual value display,
445
+ * with one space padding either side
446
+ * columns are separated with a `|` symbol, appended to the right edge of the column (effectively making each column 11 characters wide).
447
+ * note that the rightmost column is ended with this `|`.
448
+ * there is a heading/header at the top of the output table, and it uses a '**+**' to "connect" the horizontal and vertical lines being drawn
449
+ * The dashes and vertical bars set up an ASCII art-like table pattern that helps make the data more readable.
450
+ * The values are right justified with each column
451
+ * justification is essential. Everything should line up on its respective right margin.
452
+ * base headings are right justified; the word "base" is followed by the base number in a field 2 characters wide:
453
+ * you'll see "base 2" vs. "base16"; your script needs to accommodate this.
454
+ * You are to literally print out no more than 2 consecutive spaces at a time in your output statements (the **help** text is exempt from this rule). If you need more, use one of the formatting tools to generate spaces for you (**printf(1)** may be able to help you with this).
455
+ * You need to check your arguments to ensure they are present and valid. Some examples:
456
+ * starting value is a valid (whole) number
457
+ * if less than 0, or greater than 255, needs to be capped to whichever extreme it exceeds
458
+ * if -4, cap to 0
459
+ * if 300, cap to 255
460
+ * ending value is a valid (whole) number
461
+ * if less than 0, or greater than 255, needs to be capped to whichever extreme it exceeds
462
+ * if -8, cap to 0
463
+ * if 420, cap to 255
464
+ * any invalid arguments (nonsensical value in place of starting value, invalid base specification, etc.) should be silently dropped/ignored.
465
+ * default starting value is 0, default ending value is 15. If nothing else is validly provided, these are the values the script should run with.
466
+ * if starting and ending values are the same, the script will display just one line of number output (in addition to the header).
467
+ * if lacking any bases to display, silently exit (print nothing at all, not even the header, and exit with a status of 0)
468
+ * your script needs to commence with a proper **shabang** to bash; your script needs to end with an "**exit 0**" at the very end
469
+ * comments and indentation are required and necessary
470
+ * comments should explain how or why you are doing something
471
+ * indentation should be consistent throughout the script
472
+ * indentation is to be no less than 3 on-screen spaces (I recommend tabstops of 4).
473
+ * continuing with our shell scripting, your script will need to employ in a core/central way:
474
+ * variables
475
+ * command expansions
476
+ * regular expressions
477
+ * if statements
478
+ * loops
479
+ * your logic needs to:
480
+ * flow (one thing leads into the next, as best as possible)
481
+ * make sense within the given context
482
+ * avoid redundancy
483
+ * be understood by you (no grabbing snippets that seem to "work" from the internet)
484
+ * if you gain inspiration from some external resource, please cite it
485
+ * comments are a great way of demonstrating understanding (if you explain the why and how effectively, and it isn't in violation of other aspects, I'll know you are in control of things)
486
+
487
+To be sure, I'll be checking to make sure your solution follows the spirit
488
+of what this project is about (that you implement functional, flowing
489
+logic utilizing the tools and concepts we've learned, in an application
490
+that helps demonstrate your comprehension). Don't try to weasel your way
491
+out of this or cut corners. This is an opportunity to further solidify
492
+your proficiency with everything.
493
+
494
+## SPIRIT OF PROJECT
495
+
496
+The spirit of the project embodies many aspects we've been focusing on
497
+throughout the semester:
498
+
499
+ * recognizing patterns to employ effective solutions in problem solving
500
+ * utilizing concepts and tools covered
501
+ * demonstrating comprehension of concepts, tools, and problems
502
+ * employing concepts in knowledgeable and thoughtful manner
503
+ * following instructions
504
+ * implementing to specifications
505
+ * utilizing creativity
506
+ * being able to control solution via consistent, clear, and organized presentation
507
+
508
+Basically: I want your solution to be the result of an honest, genuine
509
+brainstorming process where you have (on your own) figured out a path to
510
+solving the problem, you have dabbled and experimented and figured things
511
+out, and you can command the concepts and tools with a fluency enabling
512
+you to pull off such a feat. Your solution should demonstrate the real
513
+learning that took place and experience gained.
514
+
515
+Cutting corners, avoiding work, skimping on functionality, cheating
516
+through getting others to do work for you or finding pre-packaged
517
+"answers" on the internet violates the spirit of the project, for they
518
+betray your ability to pull off the task on your own.
519
+
520
+NOTE: some have instantiated controversy in the past by utilizing AI in a
521
+manner well beyond their abilities. In such cases, submitted work is not
522
+considered your own, and thereby inviting disciplinary action. Don't fall
523
+into that trap.
524
+
525
+### IDENTIFYING SHORTCOMINGS
526
+
527
+I would also like it if you provided an inventory of what functionality
528
+is lacking or out of spec when you submit the project. The better you can
529
+describe your deviations from stated requirements, the more forgiving I
530
+may be during evaluation (versus trying to hide the shortcomings and
531
+hoping I do not discover them).
532
+
533
+The more fluent you are in describing your shortcomings on accomplishing
534
+the project (ie "I didn't know how to do this" is far from fluent), the
535
+better I will be able to gauge your understanding on a particular aspect.
536
+
537
+This can be in the form of comments in your script, or even a separate
538
+file submitted at time of submission (if a file, be sure to make mention
539
+of it in your script so I can be sure to look for it).
540
+
541
+## CONVERTING BETWEEN BASES
542
+
543
+**printf(1)** can show a value in octal (`%o`) and hexadecimal (`%x`/`%X`),
544
+but there is no format specifier for binary, nor for base 7 or base 12. The
545
+**bc(1)** calculator, however, has an `obase` (output base) setting:
546
+
547
+```
548
+lab46:~$ echo "obase=12; 196" | bc
549
+144
550
+lab46:~$ echo "obase=2; 13" | bc
551
+1101
552
+lab46:~$ echo "obase=16; 196" | bc
553
+C4
554
+lab46:~$
555
+```
556
+
557
+(Note the digits above 9 come out as uppercase letters, just as the project
558
+requires. The `obase` line must come before the number it should affect.)
559
+
560
+You could also work out the conversion by hand with the repeated-division
561
+method: divide by the base, remember the remainder, repeat with the quotient
562
+until it reaches 0, and read the remainders in reverse. Either is fine, as long
563
+as you understand what you wrote.
564
+
565
+## A FEW MORE SCRIPTING TOOLS
566
+
567
+Handling a changing list of bases, and a growing set of options, is easier
568
+with a few additional bash features. All of them are worth a visit to the
569
+**bash(1)** manual page.
570
+
571
+### arrays
572
+
573
+An array holds a list of values under one name:
574
+
575
+```
576
+bases=(2 8 10 16)
577
+bases+=(7) # append another element
578
+echo "${bases[@]}" # all elements
579
+echo "${#bases[@]}" # how many elements
580
+for b in "${bases[@]}"; do
581
+ echo "base: ${b}"
582
+done
583
+```
584
+
585
+Combine an array with other small tools to tidy it up; for instance, putting
586
+a list in order and discarding duplicates:
587
+
588
+```
589
+lab46:~$ bases=(2 8 10 16 7 8)
590
+lab46:~$ printf "%s\n" "${bases[@]}" | sort -n | uniq
591
+2
592
+7
593
+8
594
+10
595
+16
596
+lab46:~$
597
+```
598
+
599
+### functions
600
+
601
+A function packages a repeated task so you write it (and debug it) once:
602
+
603
+```
604
+show_row() {
605
+ local value="$1" # arguments arrive as $1, $2, ...
606
+ printf "%s\n" "${value}"
607
+}
608
+
609
+show_row 42
610
+```
611
+
612
+Variables marked `local` exist only inside the function. Functions must be
613
+defined before the line that calls them.
614
+
615
+### case statements
616
+
617
+A **case** compares a value against a series of patterns:
618
+
619
+```
620
+case "${arg}" in
621
+ help) usage; exit 0 ;;
622
+ by[0-9]*) echo "looks like a step" ;;
623
+ *) ;; # anything else: silently ignored
624
+esac
625
+```
626
+
627
+Be careful, though: those patterns are shell *globs*, not regular expressions.
628
+`by[0-9]*` also accepts `by5x`, because `*` means "anything at all" here. When
629
+exactness matters (and in this project it does), use a regular expression.
630
+
631
+## PATTERN MATCHING
632
+
633
+In pwn0 you used a regular expression to recognize whole numbers. In pwn1, you
634
+also have to recognize `by#`, `with#`, and `sans#` (and reject close-but-wrong
635
+things like `by`, `byx`, `by0`, `with17`, `wit8`, and `standby5`).
636
+
637
+When your pattern includes a *capture group* (parentheses), bash saves what it
638
+matched in the `BASH_REMATCH` array:
639
+
640
+```
641
+lab46:~$ arg="by13"
642
+lab46:~$ if [[ "${arg}" =~ ^by([0-9]+)$ ]]; then echo "step is ${BASH_REMATCH[1]}"; fi
643
+step is 13
644
+lab46:~$
645
+```
646
+
647
+Recognizing the *shape* of an argument is one task; deciding whether the number
648
+inside it is *acceptable* (a step of at least 1, a base from 2 to 16) is a
649
+separate task. Think about each as its own step.
650
+
651
+## SUBMISSION
652
+
653
+By successfully performing this project, you should have a fully
654
+functioning script by the name of **pwn1.sh**, which is all you need to
655
+submit for project completion (no steps file, as your "steps" file IS the
656
+script you wrote).
657
+
658
+This project continues your pwn0 work, so begin from your own pwn0.sh. A
659
+**starter script** is also available (it contains the identification block, the
660
+complete final **help** text, and notes on what is new):
661
+
662
+```
663
+$ cp /var/public/fall2026/UNIX/pwn1/pwn1.sh ~/src/SEMESTER/unix/pwn1/pwn1.sh
664
+```
665
+
666
+Your pwn1.sh must stand on its own (it may not depend on your pwn0.sh being
667
+present), and must satisfy everything in this document and in pwn0.
668
+
669
+To submit this project to me using the **submit** tool, run the following
670
+command at your lab46 prompt:
671
+
672
+```
673
+$ submit unix pwn1 pwn1.sh
674
+Submitting unix project "pwn1":
675
+ -> pwn1.sh(OK)
676
+
677
+SUCCESSFULLY SUBMITTED
678
+```
679
+
680
+You should get some sort of confirmation indicating successful submission
681
+if all went according to plan. If not, check for typos and or locational
682
+mismatches.
683
+
684
+What I'm looking for:
685
+
686
+```
687
+312:pwn1:final tally of results (312/312)
688
+*:pwn1:pwn1.sh is a proper bash script: shabang, clean exit 0, parses, no hard-coded paths [13/13]
689
+*:pwn1:pwn1.sh on help displays informative usage and exits [13/13]
690
+*:pwn1:pwn1.sh formats output with printf width specifiers (no >2 literal spaces) [13/13]
691
+*:pwn1:pwn1.sh accurately displays values in proper alignment [26/26]
692
+*:pwn1:pwn1.sh effectively utilizes variables in operations [13/13]
693
+*:pwn1:pwn1.sh effectively utilizes command expansions [13/13]
694
+*:pwn1:pwn1.sh effectively utilizes selection statements [13/13]
695
+*:pwn1:pwn1.sh effectively utilizes looping structures [13/13]
696
+*:pwn1:pwn1.sh accurately displays values across combinations of options [26/26]
697
+*:pwn1:pwn1.sh employs discrete single purpose utilities [13/13]
698
+*:pwn1:pwn1.sh links tools through pipelines / command output streams [13/13]
699
+*:pwn1:pwn1.sh uses regular expressions to validate numeric input [13/13]
700
+*:pwn1:pwn1.sh uses pattern matching to parse by#/with#/sans# options [13/13]
701
+*:pwn1:pwn1.sh range logic flexibly works forward and reverse [13/13]
702
+*:pwn1:pwn1.sh iterates by # as appropriate in given scenarios [13/13]
703
+*:pwn1:pwn1.sh includes (with#) and omits (sans#) bases as requested [13/13]
704
+*:pwn1:pwn1.sh caps starting/ending values to the 0 to 255 bounds [13/13]
705
+*:pwn1:pwn1.sh silently drops invalid arguments and still runs [13/13]
706
+*:pwn1:pwn1.sh silently exits (status 0) when no bases remain [13/13]
707
+*:pwn1:pwn1.sh comments explain how/why (header block, density, reasoning) [13/13]
708
+*:pwn1:pwn1.sh logic is organized: consistent indentation (>=3), lines within 90 chars [13/13]
709
+*:pwn1:pwn1.sh identifies shortcomings, deviations from specification [13/13]
710
+```
711
+
712
+Additionally:
713
+
714
+ * Solutions not abiding by the **SPIRIT** of the project (including
715
+ hard-coded output) will be subject to a 25% overall deduction
716
+ * Solutions not utilizing descriptive why and how **COMMENTS** will be subject to a 25% overall deduction
717
+ * Solutions not utilizing **INDENTATION** to promote scope and clarity will be subject to a 25% overall deduction
718
+ * Solutions lacking **ORGANIZATION** or that are not easy to read (within 90 char width) are subject to a 25% overall deduction