live-bootstrap

This page is produced by the version of the program scan_trace.cpp listed at the bottom of this page. The program parsers the contents of trace.txt file that is produced by running the run_chroot Bash script from a sibling directory of a clone of fosslinux/live-bootstrap (the commit 0e6133ee) in which the download-distfiles.sh script has been executed as well. (This is still work in progress.)

The code displayed on this page is not copyrighted by me but by the owners of respective repositories as also mentioned in the headers of the various files.

Binary seeds files

File /bootstrap-seeds/POSIX/x86/kaem-optional-seed

Live-bootstrap source file is 'seed/stage0-posix/bootstrap-seeds/POSIX/x86/kaem-optional-seed'.
URL: https://github.com/oriansj/bootstrap-seeds/blob/99a0ee1e544905d4aaace0de3867f13425e502f2/POSIX/x86/kaem-optional-seed
 7F 45 4C 46 01 01 01 03 00 00
 00 00 00 00 00 00 02 00 03 00
 01 00 00 00 54 80 04 08 34 00
 00 00 00 00 00 00 00 00 00 00
 34 00 20 00 01 00 00 00 00 00
 00 00 01 00 00 00 00 00 00 00
 00 80 04 08 00 80 04 08 80 02
 00 00 80 02 00 00 07 00 00 00
 01 00 00 00 58 5B 5B 85 DB 75
 06 50 BB 3A 82 04 08 31 C9 6A
 05 58 CD 80 85 C0 7E 6C A3 68
 82 04 08 58 89 E5 6A 2D 58 31
 DB CD 80 A3 78 82 04 08 A1 70
 82 04 08 E8 3F 01 00 00 89 C3
 89 D9 31 C0 A3 7C 82 04 08 A3
 6C 82 04 08 E8 95 00 00 00 85
 C0 74 05 89 01 83 C1 04 A1 6C
 82 04 08 85 C0 74 E9 39 CB 74
 CB E8 4F 00 00 00 8B 03 85 C0
 74 18 53 6A 02 58 CD 80 5B 85
 C0 7C 0D 75 12 6A 0B 58 89 EA
 89 D9 8B 1B CD 80 6A 01 5B 89
 D8 CD 80 89 C3 B9 7C 82 04 08
 31 D2 6A 07 58 CD 80 A1 7C 82
 04 08 85 C0 74 8A B8 48 82 04
 08 E8 07 01 00 00 EB D6 31 DB
 6A 01 58 CD 80 53 B8 43 82 04
 08 E8 F3 00 00 00 8B 03 E8 EC
 00 00 00 83 C3 04 6A 20 58 E8
 FE 00 00 00 39 CB 75 EA 6A 0A
 58 E8 F2 00 00 00 5B C3 53 51
 A1 74 82 04 08 E8 89 00 00 00
 89 C3 89 C1 E8 9F 00 00 00 3C
 FC 74 B5 3C 20 74 42 3C 09 74
 3E 3C 0A 75 0A 6A 01 58 A3 6C
 82 04 08 EB 30 3C 22 75 07 E8
 32 00 00 00 EB 25 3C 23 75 0F
 E8 40 00 00 00 6A 01 58 A3 6C
 82 04 08 EB 12 3C 5C 75 07 E8
 5E 00 00 00 EB 07 88 01 83 C1
 01 EB B1 39 CB 75 02 31 DB 89
 D8 59 5B C3 E8 45 00 00 00 3C
 FC 0F 84 2D FF FF FF 3C 22 74
 07 88 01 83 C1 01 EB E8 C3 E8
 2C 00 00 00 3C FC 0F 84 14 FF
 FF FF 3C 0A 75 EF C3 53 51 52
 8B 1D 78 82 04 08 01 C3 6A 2D
 58 CD 80 A1 78 82 04 08 89 1D
 78 82 04 08 5A 59 5B C3 53 51
 52 6A FC 58 50 8D 0C 24 8B 1D
 68 82 04 08 6A 03 58 6A 01 5A
 CD 80 58 3C FC 5A 59 5B C3 53
 51 89 C3 85 C0 74 12 31 C0 8A
 03 85 C0 74 0A E8 08 00 00 00
 83 C3 01 EB EE 59 5B C3 53 51
 52 50 8D 0C 24 6A 01 5B 6A 04
 58 89 DA CD 80 58 5A 59 5B C3
 6B 61 65 6D 2E 78 38 36 00 20
 2B 3E 20 00 53 75 62 70 72 6F
 63 65 73 73 20 65 72 72 6F 72
 0A 41 42 4F 52 54 49 4E 47 20
 48 41 52 44 0A 00 00 00 00 00
 00 00 00 00 00 04 00 00 00 10
 00 00 00 00 00 00 00 00 00 00

File /bootstrap-seeds/POSIX/x86/hex0-seed

Live-bootstrap source file is 'seed/stage0-posix/bootstrap-seeds/POSIX/x86/hex0-seed'.
URL: https://github.com/oriansj/bootstrap-seeds/blob/99a0ee1e544905d4aaace0de3867f13425e502f2/POSIX/x86/hex0-seed
 7F 45 4C 46 01 01 01 03 00 00
 00 00 00 00 00 00 02 00 03 00
 01 00 00 00 54 80 04 08 34 00
 00 00 00 00 00 00 00 00 00 00
 34 00 20 00 01 00 00 00 00 00
 00 00 01 00 00 00 00 00 00 00
 00 80 04 08 00 80 04 08 00 01
 00 00 00 01 00 00 07 00 00 00
 01 00 00 00 58 5B 5B 31 C9 31
 D2 6A 05 58 CD 80 89 C6 5B 66
 B9 41 02 66 BA C0 01 6A 05 58
 CD 80 89 C2 6A FF 5D 31 FF E8
 68 00 00 00 E8 1B 00 00 00 85
 C0 7C F2 85 ED 7D 06 89 C7 31
 ED EB E8 C1 E7 04 01 F8 4D E8
 39 00 00 00 EB DB 3C 23 74 1E
 3C 3B 74 1A 3C 30 7C 1F 3C 3A
 7C 1F 3C 41 7C 17 3C 47 7C 1C
 3C 61 7C 0F 3C 67 7C 12 EB 09
 E8 21 00 00 00 3C 0A 75 F7 6A
 FF 58 C3 2C 30 C3 2C 20 2C 37
 C3 89 D3 52 6A 01 5A 50 89 E1
 6A 04 58 CD 80 5B 5A C3 52 6A
 01 5A 57 89 E1 89 F3 6A 03 58
 CD 80 85 C0 74 03 58 5A C3 31
 DB 6A 01 58 CD 80

Processes

Process 1

Process 2

(Executed by Process 1)

Process 3

(Executed by Process 2)

Process 4

(Executed by Process 2)

Process 5

(Executed by Process 1)

Process 6

(Executed by Process 5)

Process 7

(Executed by Process 5)

Process 8

(Executed by Process 5)

Process 9

(Executed by Process 5)

Process 10

(Executed by Process 5)

Process 11

(Executed by Process 5)

Process 12

(Executed by Process 5)

Process 13

(Executed by Process 5)

Process 14

(Executed by Process 5)

Process 15

(Executed by Process 5)

Process 16

(Executed by Process 5)

Process 17

(Executed by Process 5)

Process 18

(Executed by Process 5)

Process 19

(Executed by Process 5)

Process 20

(Executed by Process 5)

Process 21

(Executed by Process 5)

Process 22

(Executed by Process 5)

Process 23

(Executed by Process 5)

Process 24

(Executed by Process 5)

Process 25

(Executed by Process 5)

Process 26

(Executed by Process 5)

Process 27

(Executed by Process 5)

Process 28

(Executed by Process 5)

Process 29

(Executed by Process 5)

Process 30

(Executed by Process 5)

Process 31

(Executed by Process 5)

Process 32

(Executed by Process 5)

Process 33

(Executed by Process 5)

Process 34

(Executed by Process 5)

Process 35

(Executed by Process 5)

Process 36

(Executed by Process 5)

Process 37

(Executed by Process 5)

Process 38

(Executed by Process 5)

Process 39

(Executed by Process 5)

Process 40

(Executed by Process 5)

Process 41

(Executed by Process 5)

Process 42

(Executed by Process 5)

Process 43

(Executed by Process 5)

Process 44

(Executed by Process 5)

Process 45

(Executed by Process 5)

Process 46

(Executed by Process 5)

Process 47

(Executed by Process 5)

Process 48

(Executed by Process 1)

Process 49

(Executed by Process 48)

Process 50

(Executed by Process 49)

Process 51

(Executed by Process 49)

Process 52

(Executed by Process 49)

Process 53

(Executed by Process 49)

Process 54

(Executed by Process 49)

Process 55

(Executed by Process 49)

Process 56

(Executed by Process 49)

Process 57

(Executed by Process 49)

Process 58

(Executed by Process 49)

Process 59

(Executed by Process 49)

Process 60

(Executed by Process 49)

Process 61

(Executed by Process 49)

Process 62

(Executed by Process 49)

Process 63

(Executed by Process 49)

Process 64

(Executed by Process 49)

Process 65

(Executed by Process 49)

Process 66

(Executed by Process 48)

Process 67

(Executed by Process 66)

Process 68

(Executed by Process 67)

Process 69

(Executed by Process 67)

Process 70

(Executed by Process 67)

Process 71

(Executed by Process 67)

Process 72

(Executed by Process 66)

Process 73

(Executed by Process 72)

Process 74

(Executed by Process 72)

Process 75

(Executed by Process 72)

Process 76

(Executed by Process 72)

Process 77

(Executed by Process 66)

Process 78

(Executed by Process 77)

Process 79

(Executed by Process 77)

Process 80

(Executed by Process 77)

Process 81

(Executed by Process 77)

Process 82

(Executed by Process 66)

Process 83

(Executed by Process 82)

Process 84

(Executed by Process 82)

Process 85

(Executed by Process 82)

Process 86

(Executed by Process 82)

Process 87

(Executed by Process 66)

Process 88

(Executed by Process 87)

Process 89

(Executed by Process 87)

Process 90

(Executed by Process 87)

Process 91

(Executed by Process 87)

Process 92

(Executed by Process 66)

Process 93

(Executed by Process 92)

Process 94

(Executed by Process 92)

Process 95

(Executed by Process 92)

Process 96

(Executed by Process 92)

Process 97

(Executed by Process 66)

Process 98

(Executed by Process 97)

Process 99

(Executed by Process 97)

Process 100

(Executed by Process 97)

Process 101

(Executed by Process 97)

Process 102

(Executed by Process 66)

Process 103

(Executed by Process 102)

Process 104

(Executed by Process 102)

Process 105

(Executed by Process 102)

Process 106

(Executed by Process 102)

Process 107

(Executed by Process 66)

Process 108

(Executed by Process 107)

Process 109

(Executed by Process 107)

Process 110

(Executed by Process 107)

Process 111

(Executed by Process 107)

Process 112

(Executed by Process 66)

Process 113

(Executed by Process 112)

Process 114

(Executed by Process 112)

Process 115

(Executed by Process 112)

Process 116

(Executed by Process 112)

Process 117

(Executed by Process 66)

Process 118

(Executed by Process 117)

Process 119

(Executed by Process 117)

Process 120

(Executed by Process 117)

Process 121

(Executed by Process 117)

Process 122

(Executed by Process 66)

Process 123

(Executed by Process 122)

Process 124

(Executed by Process 122)

Process 125

(Executed by Process 122)

Process 126

(Executed by Process 122)

Process 127

(Executed by Process 66)

Process 128

(Executed by Process 127)

Process 129

(Executed by Process 127)

Process 130

(Executed by Process 127)

Process 131

(Executed by Process 127)

Process 132

(Executed by Process 48)

Process 133

(Executed by Process 48)

Process 134

(Executed by Process 48)

Process 135

(Executed by Process 134)

Process 136

(Executed by Process 134)

Process 137

(Executed by Process 134)

Process 138

(Executed by Process 134)

Process 139

(Executed by Process 134)

Process 140

(Executed by Process 134)

Process 141

(Executed by Process 134)

Process 142

(Executed by Process 134)

Process 143

(Executed by Process 134)

Process 144

(Executed by Process 134)

Process 145

(Executed by Process 134)

Process 146

(Executed by Process 134)

Process 147

(Executed by Process 134)

Process 148

(Executed by Process 134)

Process 149

(Executed by Process 134)

Process 150

(Executed by Process 134)

Process 151

(Executed by Process 134)

Process 152

(Executed by Process 134)

Process 153

(Executed by Process 134)

Process 154

(Executed by Process 134)

Process 155

(Executed by Process 134)

Process 156

(Executed by Process 134)

Process 157

(Executed by Process 134)

Process 158

(Executed by Process 134)

Process 159

(Executed by Process 134)

Process 160

(Executed by Process 134)

Process 161

(Executed by Process 134)

Process 162

(Executed by Process 134)

Process 163

(Executed by Process 134)

Process 164

(Executed by Process 134)

Process 165

(Executed by Process 134)

Process 166

(Executed by Process 134)

Process 167

(Executed by Process 134)

Process 168

(Executed by Process 134)

Process 169

(Executed by Process 134)

Process 170

(Executed by Process 134)

Process 171

(Executed by Process 134)

Process 172

(Executed by Process 134)

Process 173

(Executed by Process 134)

Process 174

(Executed by Process 134)

Process 175

(Executed by Process 174)

Process 176

(Executed by Process 174)

Process 177

(Executed by Process 174)

Process 178

(Executed by Process 174)

Process 179

(Executed by Process 134)

Process 180

(Executed by Process 134)

Process 181

(Executed by Process 134)

Process 182

(Executed by Process 134)

Process 183

(Executed by Process 182)

Process 184

(Executed by Process 183)

Process 185

(Executed by Process 184)

Process 186

(Executed by Process 184)

Process 187

(Executed by Process 184)

Process 188

(Executed by Process 184)

Process 189

(Executed by Process 183)

Process 190

(Executed by Process 183)

Process 191

(Executed by Process 182)

Process 192

(Executed by Process 191)

Process 193

(Executed by Process 192)

Process 194

(Executed by Process 192)

Process 195

(Executed by Process 192)

Process 196

(Executed by Process 192)

Process 197

(Executed by Process 191)

Process 198

(Executed by Process 191)

Process 199

(Executed by Process 182)

Process 200

(Executed by Process 199)

Process 201

(Executed by Process 199)

Process 202

(Executed by Process 199)

Process 203

(Executed by Process 199)

Process 204

(Executed by Process 199)

Process 205

(Executed by Process 199)

Process 206

(Executed by Process 199)

Process 207

(Executed by Process 199)

Process 208

(Executed by Process 199)

Process 209

(Executed by Process 199)

Process 210

(Executed by Process 199)

Process 211

(Executed by Process 199)

Process 212

(Executed by Process 199)

Process 213

(Executed by Process 199)

Process 214

(Executed by Process 199)

Process 215

(Executed by Process 199)

Process 216

(Executed by Process 199)

Process 217

(Executed by Process 199)

Process 218

(Executed by Process 199)

Process 219

(Executed by Process 199)

Process 220

(Executed by Process 219)

Process 221

(Executed by Process 220)

Process 222

(Executed by Process 220)

Process 223

(Executed by Process 220)

Process 224

(Executed by Process 220)

Process 225

(Executed by Process 220)

Process 226

(Executed by Process 220)

Process 227

(Executed by Process 220)

Process 228

(Executed by Process 220)

Process 229

(Executed by Process 199)

Process 230

(Executed by Process 199)

Process 231

(Executed by Process 199)

Process 232

(Executed by Process 199)

Process 233

(Executed by Process 199)

Process 234

(Executed by Process 199)

Process 235

(Executed by Process 199)

Process 236

(Executed by Process 199)

Process 237

(Executed by Process 199)

Process 238

(Executed by Process 199)

Process 239

(Executed by Process 199)

Process 240

(Executed by Process 199)

Process 241

(Executed by Process 199)

Process 242

(Executed by Process 199)

Process 243

(Executed by Process 242)

Process 244

(Executed by Process 199)

Process 245

(Executed by Process 244)

Process 246

(Executed by Process 199)

Process 247

(Executed by Process 246)

Process 248

(Executed by Process 199)

Process 249

(Executed by Process 248)

Process 250

(Executed by Process 199)

Process 251

(Executed by Process 250)

Process 252

(Executed by Process 199)

Process 253

(Executed by Process 252)

Process 254

(Executed by Process 199)

Process 255

(Executed by Process 254)

Process 256

(Executed by Process 199)

Process 257

(Executed by Process 256)

Process 258

(Executed by Process 199)

Process 259

(Executed by Process 258)

Process 260

(Executed by Process 199)

Process 261

(Executed by Process 260)

Process 262

(Executed by Process 199)

Process 263

(Executed by Process 199)

Process 264

(Executed by Process 199)

Process 265

(Executed by Process 264)

Process 266

(Executed by Process 199)

Process 267

(Executed by Process 199)

Process 268

(Executed by Process 199)

Process 269

(Executed by Process 268)

Process 270

(Executed by Process 199)

Process 271

(Executed by Process 270)

Process 272

(Executed by Process 199)

Process 273

(Executed by Process 272)

Process 274

(Executed by Process 199)

Process 275

(Executed by Process 274)

Process 276

(Executed by Process 199)

Process 277

(Executed by Process 276)

Process 278

(Executed by Process 199)

Process 279

(Executed by Process 278)

Process 280

(Executed by Process 199)

Process 281

(Executed by Process 280)

Process 282

(Executed by Process 199)

Process 283

(Executed by Process 282)

Process 284

(Executed by Process 199)

Process 285

(Executed by Process 284)

Process 286

(Executed by Process 199)

Process 287

(Executed by Process 286)

Process 288

(Executed by Process 199)

Process 289

(Executed by Process 288)

Process 290

(Executed by Process 199)

Process 291

(Executed by Process 290)

Process 292

(Executed by Process 199)

Process 293

(Executed by Process 292)

Process 294

(Executed by Process 199)

Process 295

(Executed by Process 294)

Process 296

(Executed by Process 199)

Process 297

(Executed by Process 296)

Process 298

(Executed by Process 199)

Process 299

(Executed by Process 298)

Process 300

(Executed by Process 199)

Process 301

(Executed by Process 300)

Process 302

(Executed by Process 199)

Process 303

(Executed by Process 302)

Process 304

(Executed by Process 199)

Process 305

(Executed by Process 304)

Process 306

(Executed by Process 199)

Process 307

(Executed by Process 306)

Process 308

(Executed by Process 199)

Process 309

(Executed by Process 308)

Process 310

(Executed by Process 199)

Process 311

(Executed by Process 310)

Process 312

(Executed by Process 199)

Process 313

(Executed by Process 312)

Process 314

(Executed by Process 199)

Process 315

(Executed by Process 314)

Process 316

(Executed by Process 199)

Process 317

(Executed by Process 316)

Process 318

(Executed by Process 199)

Process 319

(Executed by Process 318)

Process 320

(Executed by Process 199)

Process 321

(Executed by Process 320)

Process 322

(Executed by Process 199)

Process 323

(Executed by Process 322)

Process 324

(Executed by Process 199)

Process 325

(Executed by Process 324)

Process 326

(Executed by Process 199)

Process 327

(Executed by Process 326)

Process 328

(Executed by Process 199)

Process 329

(Executed by Process 328)

Process 330

(Executed by Process 199)

Process 331

(Executed by Process 330)

Process 332

(Executed by Process 199)

Process 333

(Executed by Process 332)

Process 334

(Executed by Process 199)

Process 335

(Executed by Process 334)

Process 336

(Executed by Process 199)

Process 337

(Executed by Process 336)

Process 338

(Executed by Process 199)

Process 339

(Executed by Process 338)

Process 340

(Executed by Process 199)

Process 341

(Executed by Process 340)

Process 342

(Executed by Process 199)

Process 343

(Executed by Process 342)

Process 344

(Executed by Process 199)

Process 345

(Executed by Process 344)

Process 346

(Executed by Process 199)

Process 347

(Executed by Process 346)

Process 348

(Executed by Process 199)

Process 349

(Executed by Process 348)

Process 350

(Executed by Process 199)

Process 351

(Executed by Process 350)

Process 352

(Executed by Process 199)

Process 353

(Executed by Process 352)

Process 354

(Executed by Process 199)

Process 355

(Executed by Process 354)

Process 356

(Executed by Process 199)

Process 357

(Executed by Process 356)

Process 358

(Executed by Process 199)

Process 359

(Executed by Process 358)

Process 360

(Executed by Process 199)

Process 361

(Executed by Process 360)

Process 362

(Executed by Process 199)

Process 363

(Executed by Process 362)

Process 364

(Executed by Process 199)

Process 365

(Executed by Process 364)

Process 366

(Executed by Process 199)

Process 367

(Executed by Process 366)

Process 368

(Executed by Process 199)

Process 369

(Executed by Process 368)

Process 370

(Executed by Process 199)

Process 371

(Executed by Process 370)

Process 372

(Executed by Process 199)

Process 373

(Executed by Process 372)

Process 374

(Executed by Process 199)

Process 375

(Executed by Process 374)

Process 376

(Executed by Process 199)

Process 377

(Executed by Process 376)

Process 378

(Executed by Process 199)

Process 379

(Executed by Process 378)

Process 380

(Executed by Process 199)

Process 381

(Executed by Process 380)

Process 382

(Executed by Process 199)

Process 383

(Executed by Process 382)

Process 384

(Executed by Process 199)

Process 385

(Executed by Process 384)

Process 386

(Executed by Process 199)

Process 387

(Executed by Process 386)

Process 388

(Executed by Process 199)

Process 389

(Executed by Process 388)

Process 390

(Executed by Process 199)

Process 391

(Executed by Process 390)

Process 392

(Executed by Process 199)

Process 393

(Executed by Process 392)

Process 394

(Executed by Process 199)

Process 395

(Executed by Process 394)

Process 396

(Executed by Process 199)

Process 397

(Executed by Process 396)

Process 398

(Executed by Process 199)

Process 399

(Executed by Process 398)

Process 400

(Executed by Process 199)

Process 401

(Executed by Process 400)

Process 402

(Executed by Process 199)

Process 403

(Executed by Process 402)

Process 404

(Executed by Process 199)

Process 405

(Executed by Process 404)

Process 406

(Executed by Process 199)

Process 407

(Executed by Process 406)

Process 408

(Executed by Process 199)

Process 409

(Executed by Process 408)

Process 410

(Executed by Process 199)

Process 411

(Executed by Process 410)

Process 412

(Executed by Process 199)

Process 413

(Executed by Process 412)

Process 414

(Executed by Process 199)

Process 415

(Executed by Process 414)

Process 416

(Executed by Process 199)

Process 417

(Executed by Process 416)

Process 418

(Executed by Process 199)

Process 419

(Executed by Process 418)

Process 420

(Executed by Process 199)

Process 421

(Executed by Process 420)

Process 422

(Executed by Process 199)

Process 423

(Executed by Process 422)

Process 424

(Executed by Process 199)

Process 425

(Executed by Process 424)

Process 426

(Executed by Process 199)

Process 427

(Executed by Process 426)

Process 428

(Executed by Process 199)

Process 429

(Executed by Process 428)

Process 430

(Executed by Process 199)

Process 431

(Executed by Process 430)

Process 432

(Executed by Process 199)

Process 433

(Executed by Process 432)

Process 434

(Executed by Process 199)

Process 435

(Executed by Process 434)

Process 436

(Executed by Process 199)

Process 437

(Executed by Process 436)

Process 438

(Executed by Process 199)

Process 439

(Executed by Process 438)

Process 440

(Executed by Process 199)

Process 441

(Executed by Process 440)

Process 442

(Executed by Process 199)

Process 443

(Executed by Process 442)

Process 444

(Executed by Process 199)

Process 445

(Executed by Process 444)

Process 446

(Executed by Process 199)

Process 447

(Executed by Process 446)

Process 448

(Executed by Process 199)

Process 449

(Executed by Process 448)

Process 450

(Executed by Process 199)

Process 451

(Executed by Process 450)

Process 452

(Executed by Process 199)

Process 453

(Executed by Process 452)

Process 454

(Executed by Process 199)

Process 455

(Executed by Process 454)

Process 456

(Executed by Process 199)

Process 457

(Executed by Process 456)

Process 458

(Executed by Process 199)

Process 459

(Executed by Process 458)

Process 460

(Executed by Process 199)

Process 461

(Executed by Process 199)

Process 462

(Executed by Process 199)

Process 463

(Executed by Process 462)

Process 464

(Executed by Process 199)

Process 465

(Executed by Process 464)

Process 466

(Executed by Process 199)

Process 467

(Executed by Process 466)

Process 468

(Executed by Process 199)

Process 469

(Executed by Process 468)

Process 470

(Executed by Process 199)

Process 471

(Executed by Process 470)

Process 472

(Executed by Process 199)

Process 473

(Executed by Process 472)

Process 474

(Executed by Process 199)

Process 475

(Executed by Process 474)

Process 476

(Executed by Process 199)

Process 477

(Executed by Process 476)

Process 478

(Executed by Process 199)

Process 479

(Executed by Process 478)

Process 480

(Executed by Process 199)

Process 481

(Executed by Process 480)

Process 482

(Executed by Process 199)

Process 483

(Executed by Process 482)

Process 484

(Executed by Process 199)

Process 485

(Executed by Process 484)

Process 486

(Executed by Process 199)

Process 487

(Executed by Process 486)

Process 488

(Executed by Process 199)

Process 489

(Executed by Process 488)

Process 490

(Executed by Process 199)

Process 491

(Executed by Process 490)

Process 492

(Executed by Process 199)

Process 493

(Executed by Process 492)

Process 494

(Executed by Process 199)

Process 495

(Executed by Process 494)

Process 496

(Executed by Process 199)

Process 497

(Executed by Process 496)

Process 498

(Executed by Process 199)

Process 499

(Executed by Process 498)

Process 500

(Executed by Process 199)

Process 501

(Executed by Process 500)

Process 502

(Executed by Process 199)

Process 503

(Executed by Process 502)

Process 504

(Executed by Process 199)

Process 505

(Executed by Process 504)

Process 506

(Executed by Process 199)

Process 507

(Executed by Process 506)

Process 508

(Executed by Process 199)

Process 509

(Executed by Process 508)

Process 510

(Executed by Process 199)

Process 511

(Executed by Process 510)

Process 512

(Executed by Process 199)

Process 513

(Executed by Process 512)

Process 514

(Executed by Process 199)

Process 515

(Executed by Process 514)

Process 516

(Executed by Process 199)

Process 517

(Executed by Process 516)

Process 518

(Executed by Process 199)

Process 519

(Executed by Process 518)

Process 520

(Executed by Process 199)

Process 521

(Executed by Process 520)

Process 522

(Executed by Process 199)

Process 523

(Executed by Process 522)

Process 524

(Executed by Process 199)

Process 525

(Executed by Process 524)

Process 526

(Executed by Process 199)

Process 527

(Executed by Process 526)

Process 528

(Executed by Process 199)

Process 529

(Executed by Process 528)

Process 530

(Executed by Process 199)

Process 531

(Executed by Process 530)

Process 532

(Executed by Process 199)

Process 533

(Executed by Process 532)

Process 534

(Executed by Process 199)

Process 535

(Executed by Process 534)

Process 536

(Executed by Process 199)

Process 537

(Executed by Process 536)

Process 538

(Executed by Process 199)

Process 539

(Executed by Process 538)

Process 540

(Executed by Process 199)

Process 541

(Executed by Process 540)

Process 542

(Executed by Process 199)

Process 543

(Executed by Process 542)

Process 544

(Executed by Process 199)

Process 545

(Executed by Process 544)

Process 546

(Executed by Process 199)

Process 547

(Executed by Process 546)

Process 548

(Executed by Process 199)

Process 549

(Executed by Process 548)

Process 550

(Executed by Process 199)

Process 551

(Executed by Process 550)

Process 552

(Executed by Process 199)

Process 553

(Executed by Process 552)

Process 554

(Executed by Process 199)

Process 555

(Executed by Process 554)

Process 556

(Executed by Process 199)

Process 557

(Executed by Process 556)

Process 558

(Executed by Process 199)

Process 559

(Executed by Process 558)

Process 560

(Executed by Process 199)

Process 561

(Executed by Process 560)

Process 562

(Executed by Process 199)

Process 563

(Executed by Process 562)

Process 564

(Executed by Process 199)

Process 565

(Executed by Process 564)

Process 566

(Executed by Process 199)

Process 567

(Executed by Process 566)

Process 568

(Executed by Process 199)

Process 569

(Executed by Process 568)

Process 570

(Executed by Process 199)

Process 571

(Executed by Process 570)

Process 572

(Executed by Process 199)

Process 573

(Executed by Process 572)

Process 574

(Executed by Process 199)

Process 575

(Executed by Process 574)

Process 576

(Executed by Process 199)

Process 577

(Executed by Process 576)

Process 578

(Executed by Process 199)

Process 579

(Executed by Process 578)

Process 580

(Executed by Process 199)

Process 581

(Executed by Process 580)

Process 582

(Executed by Process 199)

Process 583

(Executed by Process 582)

Process 584

(Executed by Process 199)

Process 585

(Executed by Process 584)

Process 586

(Executed by Process 199)

Process 587

(Executed by Process 586)

Process 588

(Executed by Process 199)

Process 589

(Executed by Process 588)

Process 590

(Executed by Process 199)

Process 591

(Executed by Process 590)

Process 592

(Executed by Process 199)

Process 593

(Executed by Process 592)

Process 594

(Executed by Process 199)

Process 595

(Executed by Process 594)

Process 596

(Executed by Process 199)

Process 597

(Executed by Process 596)

Process 598

(Executed by Process 199)

Process 599

(Executed by Process 598)

Process 600

(Executed by Process 199)

Process 601

(Executed by Process 600)

Process 602

(Executed by Process 199)

Process 603

(Executed by Process 602)

Process 604

(Executed by Process 199)

Process 605

(Executed by Process 604)

Process 606

(Executed by Process 199)

Process 607

(Executed by Process 606)

Process 608

(Executed by Process 199)

Process 609

(Executed by Process 608)

Process 610

(Executed by Process 199)

Process 611

(Executed by Process 610)

Process 612

(Executed by Process 199)

Process 613

(Executed by Process 199)

Process 614

(Executed by Process 199)

Process 615

(Executed by Process 199)

Process 616

(Executed by Process 199)

Process 617

(Executed by Process 199)

Process 618

(Executed by Process 199)

Process 619

(Executed by Process 199)

Process 620

(Executed by Process 199)

Process 621

(Executed by Process 199)

Process 622

(Executed by Process 199)

Process 623

(Executed by Process 199)

Process 624

(Executed by Process 199)

Process 625

(Executed by Process 199)

Process 626

(Executed by Process 199)

Process 627

(Executed by Process 199)

Process 628

(Executed by Process 199)

Process 629

(Executed by Process 199)

Process 630

(Executed by Process 199)

Process 631

(Executed by Process 199)

Process 632

(Executed by Process 199)

Process 633

(Executed by Process 199)

Process 634

(Executed by Process 199)

Process 635

(Executed by Process 199)

Process 636

(Executed by Process 199)

Process 637

(Executed by Process 199)

Process 638

(Executed by Process 199)

Process 639

(Executed by Process 199)

Process 640

(Executed by Process 199)

Process 641

(Executed by Process 199)

Process 642

(Executed by Process 199)

Process 643

(Executed by Process 199)

Process 644

(Executed by Process 199)

Process 645

(Executed by Process 199)

Process 646

(Executed by Process 199)

Process 647

(Executed by Process 199)

Process 648

(Executed by Process 199)

Process 649

(Executed by Process 199)

Process 650

(Executed by Process 199)

Process 651

(Executed by Process 199)

Process 652

(Executed by Process 199)

Process 653

(Executed by Process 199)

Process 654

(Executed by Process 199)

Process 655

(Executed by Process 199)

Process 656

(Executed by Process 199)

Process 657

(Executed by Process 199)

Process 658

(Executed by Process 199)

Process 659

(Executed by Process 199)

Process 660

(Executed by Process 199)

Process 661

(Executed by Process 199)

Process 662

(Executed by Process 199)

Process 663

(Executed by Process 199)

Process 664

(Executed by Process 199)

Process 665

(Executed by Process 199)

Process 666

(Executed by Process 199)

Process 667

(Executed by Process 199)

Process 668

(Executed by Process 199)

Process 669

(Executed by Process 199)

Process 670

(Executed by Process 199)

Process 671

(Executed by Process 199)

Process 672

(Executed by Process 199)

Process 673

(Executed by Process 199)

Process 674

(Executed by Process 199)

Process 675

(Executed by Process 199)

Process 676

(Executed by Process 199)

Process 677

(Executed by Process 199)

Process 678

(Executed by Process 199)

Process 679

(Executed by Process 199)

Process 680

(Executed by Process 199)

Process 681

(Executed by Process 199)

Process 682

(Executed by Process 199)

Process 683

(Executed by Process 199)

Process 684

(Executed by Process 199)

Process 685

(Executed by Process 199)

Process 686

(Executed by Process 199)

Process 687

(Executed by Process 199)

Process 688

(Executed by Process 199)

Process 689

(Executed by Process 199)

Process 690

(Executed by Process 199)

Process 691

(Executed by Process 199)

Process 692

(Executed by Process 199)

Process 693

(Executed by Process 199)

Process 694

(Executed by Process 182)

Process 695

(Executed by Process 694)

Process 696

(Executed by Process 694)

Process 697

(Executed by Process 694)

Process 698

(Executed by Process 694)

Process 699

(Executed by Process 694)

Process 700

(Executed by Process 694)

Process 701

(Executed by Process 694)

Process 702

(Executed by Process 694)

Process 703

(Executed by Process 694)

Process 704

(Executed by Process 694)

Process 705

(Executed by Process 694)

Process 706

(Executed by Process 694)

Process 707

(Executed by Process 694)

Process 708

(Executed by Process 694)

Process 709

(Executed by Process 694)

Process 710

(Executed by Process 694)

Process 711

(Executed by Process 710)

Process 712

(Executed by Process 710)

Process 713

(Executed by Process 694)

Process 714

(Executed by Process 694)

Process 715

(Executed by Process 694)

Process 716

(Executed by Process 694)

Process 717

(Executed by Process 694)

Process 718

(Executed by Process 694)

Process 719

(Executed by Process 694)

Process 720

(Executed by Process 694)

Process 721

(Executed by Process 694)

Process 722

(Executed by Process 694)

Process 723

(Executed by Process 694)

Process 724

(Executed by Process 694)

Process 725

(Executed by Process 694)

Process 726

(Executed by Process 694)

Process 727

(Executed by Process 694)

Process 728

(Executed by Process 694)

Process 729

(Executed by Process 694)

Process 730

(Executed by Process 694)

Process 731

(Executed by Process 694)

Sources used:

Process 732

(Executed by Process 694)

Process 733

(Executed by Process 694)

Process 734

(Executed by Process 694)

Input source files

File /kaem.x86

Live-bootstrap source file is 'seed/stage0-posix/kaem.x86'.
URL: https://github.com/oriansj/stage0-posix/blob/779e5424d4b55fe9b7faea2285ae8b6486df0433/kaem.x86
#! /usr/bin/env bash
# Mes --- Maxwell Equations of Software
# Copyright © 2017,2019 Jan Nieuwenhuizen <janneke@gnu.org>
# Copyright © 2017,2019 Jeremiah Orians
#
# This file is part of Mes.
#
# Mes is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or (at
# your option) any later version.
#
# Mes is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Mes.  If not, see <http://www.gnu.org/licenses/>.



# Can also be run by kaem or any other shell of your personal choice
# To run in kaem simply: kaem --verbose --strict

##################################################
# Phase 0-11 Build hex0 from bootstrapped binary #
##################################################

./bootstrap-seeds/POSIX/x86/kaem-optional-seed ./x86/mescc-tools-seed-kaem.kaem
./x86/artifact/kaem-0 ./x86/mescc-tools-mini-kaem.kaem

#######################################
# Run remaining phases with full kaem #
#######################################
./x86/bin/kaem --verbose --strict --file ./x86/kaem.run

File /x86/mescc-tools-seed-kaem.kaem

Live-bootstrap source file is 'seed/stage0-posix/x86/mescc-tools-seed-kaem.kaem'.
URL: https://github.com/oriansj/stage0-posix-x86/blob/83508012c952e3aa3f70a89fd7d85d9a1af6f305/mescc-tools-seed-kaem.kaem
#! /usr/bin/env bash
# Mes --- Maxwell Equations of Software
# Copyright © 2017,2019 Jan Nieuwenhuizen <janneke@gnu.org>
# Copyright © 2017,2019 Jeremiah Orians
#
# This file is part of Mes.
#
# Mes is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or (at
# your option) any later version.
#
# Mes is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Mes.  If not, see <http://www.gnu.org/licenses/>.



# Can also be run by kaem or any other shell of your personal choice
# To run in kaem simply: kaem --verbose --strict
# Warning all binaries prior to the use of blood-elf will not be readable by
# Objdump, you may need to use ndism or gdb to view the assembly in the binary.



###############################################
# Phase-0 Build hex0 from bootstrapped binary #
###############################################
./bootstrap-seeds/POSIX/x86/hex0-seed ./x86/hex0_x86.hex0 ./x86/artifact/hex0
# hex0 should have the exact same checksum as hex0-seed as they are both supposed
# to be built from hex0_x86.hex0 and by definition must be identical

#########################################
# Phase-0b Build minimal kaem from hex0 #
#########################################
./x86/artifact/hex0 ./x86/kaem-minimal.hex0 ./x86/artifact/kaem-0
# for checksum validation reasons

File /x86/hex0_x86.hex0

Live-bootstrap source file is 'seed/stage0-posix/x86/hex0_x86.hex0'.
URL: https://github.com/oriansj/stage0-posix-x86/blob/83508012c952e3aa3f70a89fd7d85d9a1af6f305/hex0_x86.hex0
# SPDX-FileCopyrightText: 2019 Jeremiah Orians
# SPDX-FileCopyrightText: 2022 Andrius Å tikonas
#
# SPDX-License-Identifier: GPL-3.0-or-later

## ELF Header
#:ELF_base
7F 45 4C 46                     # e_ident[EI_MAG0-3] ELF's magic number

01                              # e_ident[EI_CLASS] Indicating 32 bit
01                              # e_ident[EI_DATA] Indicating little endianness
01                              # e_ident[EI_VERSION] Indicating original elf

03                              # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                              # e_ident[EI_ABIVERSION] Set at 0 because no one cares

00 00 00 00 00 00 00            # e_ident[EI_PAD]

02 00                           # e_type Indicating Executable
03 00                           # e_machine Indicating x86
01 00 00 00                     # e_version Indicating original elf

54 80 04 08                     # e_entry Address of the entry point
34 00 00 00                     # e_phoff Address of program header table
00 00 00 00                     # e_shoff Address of section header table

00 00 00 00                     # e_flags

34 00                           # e_ehsize Indicating our 52 Byte header

20 00                           # e_phentsize size of a program header table
01 00                           # e_phnum number of entries in program table

00 00                           # e_shentsize size of a section header table
00 00                           # e_shnum number of entries in section table

00 00                           # e_shstrndx index of the section names

## Program Header
#:ELF_program_headers
#:ELF_program_header__text
01 00 00 00                     # ph_type: PT-LOAD = 1
00 00 00 00                     # ph_offset

00 80 04 08                     # ph_vaddr
00 80 04 08                     # ph_physaddr

00 01 00 00                     # ph_filesz
00 01 00 00                     # ph_memsz

07 00 00 00                     # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                     # ph_align

#:ELF_text

# Where the ELF Header is going to hit
# Simply jump to _start
# Our main function
# :_start ; (0x8048054)
    58                          ; pop_eax                     # Get the number of arguments
    5B                          ; pop_ebx                     # Get the program name
    5B                          ; pop_ebx                     # Get the actual input name
    31C9                        ; xor_ecx,ecx                 # prepare read_only, ecx = 0
    31D2                        ; xor_edx,edx                 # Extra sure, edx = 0
    6A 05                       ; push !5                     # prepare to set eax to 5
    58                          ; pop_eax                     # the syscall number for open()
    CD 80                       ; int !0x80                   # Now open that damn file
    89C6                        ; mov_esi,eax                 # Preserve the file pointer we were given

    5B                          ; pop_ebx                     # Get the actual output name
    66B9 4102                   ; mov_cx, @577                # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    66BA C001                   ; mov_dx, @448                # Prepare file as RWX for owner only (700 in octal)
    6A 05                       ; push !5                     # prepare to set eax to 5
    58                          ; pop_eax                     # the syscall number for open()
    CD 80                       ; int !0x80                   # Now open that damn file
    89C2                        ; mov_edx,eax                 # Preserve the file pointer we were given

    # Our flag for byte processing
    6A FF                       ; push !-1
    5D                          ; pop_ebp                     # ebp = -1

    # temp storage for the sum
    31FF                        ; xor_edi,edi                 # edi = 0

#:loop ; (0x8048077)
    # Read a byte
    E8 68000000                 ; call %Read_byte

    # process byte
    E8 1B000000                 ; call %hex

    # Deal with -1 values
    85C0                        ; test_eax,eax
    7C F2                       ; jl !loop

    # deal with toggle
    85ED                        ; test_ebp,ebp                # jump if ebp >= 0
    7D 06                       ; jge !print

    # process first byte of pair
    89C7                        ; mov_edi,eax
    31ED                        ; xor_ebp,ebp                 # ebp = 0
    EB E8                       ; jmp !loop

# process second byte of pair
#:print ; (0x804808F)
    # update the sum and store in output
    C1E7 04                     ; shl_edi, !4
    01F8                        ; add_eax,edi

    # flip the toggle
    4D                          ; dec_ebp                     # ebp = -1

    E8 39000000                 ; call %write_byte

    EB DB                       ; jmp !loop

#:hex ; (0x804809C)
    # Purge Comment Lines (#)
    3C 23                       ; cmp_al, !35
    74 1E                       ; je !purge_comment

    # Purge Comment Lines (;)
    3C 3B                       ; cmp_al, !59
    74 1A                       ; je !purge_comment

    # deal all ascii less than 0
    3C 30                       ; cmp_al, !48
    7C 1F                       ; jl !ascii_other

    # deal with 0-9
    3C 3A                       ; cmp_al, !58
    7C 1F                       ; jl !ascii_num

    # deal with all ascii less than A
    3C 41                       ; cmp_al, !65
    7C 17                       ; jl !ascii_other

    # deal with A-F
    3C 47                       ; cmp_al, !71
    7C 1C                       ; jl !ascii_high

    # deal with all ascii less than a
    3C 61                       ; cmp_al, !97
    7C 0F                       ; jl !ascii_other

    # deal with a-f
    3C 67                       ; cmp_al, !103
    7C 12                       ; jl !ascii_low

    # The rest that remains needs to be ignored
    EB 09                       ; jmp !ascii_other

#:purge_comment ; (0x80480BE)
    # Read a byte
    E8 21000000                 ; call %Read_byte

    # Loop if not LF
    3C 0A                       ; cmp_al, !10
    75 F7                       ; jne !purge_comment

    # Otherwise return -1

#:ascii_other ; (0x80480C7)
    6A FF                       ; push !-1
    58                          ; pop_eax                     # return -1
    C3                          ; ret

#:ascii_num ; (0x80480CB)
    2C 30                       ; sub_al, !48
    C3                          ; ret

#:ascii_low ; (0x80480CE)
    2C 20                       ; sub_al, !32                 # convert to uppercase

#:ascii_high ; (0x80480D0)
    2C 37                       ; sub_al, !55
    C3                          ; ret

# Writes byte stored in al
#:write_byte ; (0x80480D3)
    # Print our Hex
    89D3                        ; mov_ebx,edx                 # Where are we writing to
    52                          ; push_edx                    # protect fout
    6A 01                       ; push !1                     # prepare to set edx to 1
    5A                          ; pop_edx                     # set the size of chars we want
    50                          ; push_eax                    # Move output to stack
    89E1                        ; mov_ecx,esp                 # What we are writing
    6A 04                       ; push !4                     # prepare to set eax to 4
    58                          ; pop_eax                     # the syscall number for write
    CD 80                       ; int !0x80                   # call the Kernel
    5B                          ; pop_ebx                     # deallocate stack
    5A                          ; pop_edx                     # restore fout
    C3                          ; ret

#:Read_byte ; (0x80480E4)
    # Attempt to read 1 byte from Input file
    52                          ; push_edx                    # protect fout
    6A 01                       ; push !1                     # prepare to set edx to 1
    5A                          ; pop_edx                     # set the size of chars we want
    57                          ; push_edi                    # allocate stack
    89E1                        ; mov_ecx,esp                 # Where to put it
    89F3                        ; mov_ebx,esi                 # Where are we reading from
    6A 03                       ; push !3                     # prepare to set eax to 3
    58                          ; pop_eax                     # the syscall number for read
    CD 80                       ; int !0x80                   # call the Kernel

    85C0                        ; test_eax,eax                # check what we got
    74 03                       ; je !Done                    # Got EOF call it done

    # load byte
    58                          ; pop_eax                     # load char
    5A                          ; pop_edx                     # restore fout
    C3                          ; ret

#:Done ; (0x80480F9)
    # program completed Successfully
    31DB                        ; xor_ebx,ebx                 # All is well, ebx = 0
    6A 01                       ; push !1
    58                          ; pop_eax                     # put the exit syscall number in eax
    CD 80                       ; int !0x80                   # Call it a good day

#:ELF_end

File /x86/kaem-minimal.hex0

Live-bootstrap source file is 'seed/stage0-posix/x86/kaem-minimal.hex0'.
URL: https://github.com/oriansj/stage0-posix-x86/blob/83508012c952e3aa3f70a89fd7d85d9a1af6f305/kaem-minimal.hex0
# SPDX-FileCopyrightText: 2020 Jeremiah Orians
# SPDX-FileCopyrightText: 2022 Andrius Å tikonas
#
# SPDX-License-Identifier: GPL-3.0-or-later

## ELF Header
# :ELF_base ; (0x8048000)
    7F 45 4C 46                     # e_ident[EI_MAG0-3] ELF's magic number

    01                              # e_ident[EI_CLASS] Indicating 32 bit
    01                              # e_ident[EI_DATA] Indicating little endianness
    01                              # e_ident[EI_VERSION] Indicating original elf

    03                              # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
    00                              # e_ident[EI_ABIVERSION] Set at 0 because none cares

    00 00 00 00 00 00 00            # e_ident[EI_PAD]

    02 00                           # e_type Indicating Executable
    03 00                           # e_machine Indicating x86
    01 00 00 00                     # e_version Indicating original elf

    54 80 04 08                     # e_entry Address of the entry point
    34 00 00 00                     # e_phoff Address of program header table
    00 00 00 00                     # e_shoff Address of section header table

    00 00 00 00                     # e_flags

    34 00                           # e_ehsize Indicating our 52 Byte header

    20 00                           # e_phentsize size of a program header table
    01 00                           # e_phnum number of entries in program table

    00 00                           # e_shentsize size of a section header table
    00 00                           # e_shnum number of entries in section table

    00 00                           # e_shstrndx index of the section names

## Program Header
# :ELF_program_headers
# :ELF_program_header__text
    01 00 00 00                     # ph_type: PT-LOAD = 1
    00 00 00 00                     # ph_offset

    00 80 04 08                     # ph_vaddr
    00 80 04 08                     # ph_physaddr

    80 02 00 00                     # ph_filesz
    80 02 00 00                     # ph_memsz

    07 00 00 00                     # ph_flags: PF-X|PF-W|PF-R = 7
    01 00 00 00                     # ph_align

# :ELF_text

# :_start ; (0x8048054)
    58                              ; pop_eax                           # Get the number of arguments
    5B                              ; pop_ebx                           # Get the program name
    5B                              ; pop_ebx                           # Get the actual input name
    85DB                            ; test_ebx,ebx                      # Check for missing output
    75 06                           ; jne !_start_out                   # Have real input
    50                              ; push_eax                          # Need to adjust stack
    BB 3A820408                     ; mov_ebx, &default_file            # Use "kaem.x86"

# :_start_out ; (0x8048061)
    31C9                            ; xor_ecx,ecx                       # prepare read_only
    6A 05                           ; push !5
    58                              ; pop_eax                           # the syscall number for open()
    CD80                            ; int !0x80                         # Now open that damn file

    85C0                            ; test_eax,eax                      # IF NULL We couldn't open the file
    7E 6C                           ; jle !Exit_Failure                 # Abort hard
    A3 68820408                     ; mov_[DWORD],eax &script           # Set input pointer

    58                              ; pop_eax                           # Get start of envp
    89E5                            ; mov_ebp,esp                       # Protect envp

    6A 2D                           ; push !45
    58                              ; pop_eax                           # the Syscall # for SYS_BRK
    31DB                            ; xor_ebx,ebx                       # Get current brk
    CD80                            ; int !0x80                         # Let the kernel do the work
    A3 78820408                     ; mov_[DWORD],eax &MALLOC           # Set our malloc pointer

    # Where the main work gets done
    # Using EBX for tokens and ECX for tokens[i]
# :main_loop ; (0x8048080)
    A1 70820408                     ; mov_eax,[DWORD] &max_args         # Using 256 char* of space
    E8 3F010000                     ; call %malloc                      # get it
    89C3                            ; mov_ebx,eax                       # set tokens

    89D9                            ; mov_ecx,ebx                       # I = 0
    31C0                            ; xor_eax,eax                       # Using 0
    A3 7C820408                     ; mov_[DWORD],eax &status           # status = 0
    A3 6C820408                     ; mov_[DWORD],eax &command_done     # command_done = 0

    # Using EAX for result and EBX for tokens[i]
# :collect_command ; (0x804809A)
    E8 95000000                     ; call %collect_token               # Get another token
    85C0                            ; test_eax,eax                      # if NULL == result
    74 05                           ; je !collect_command_comment       # It is a comment, don't store

    8901                            ; mov_[ecx],eax                     # tokens[i] = result
    83C1 04                         ; add_ecx, !4                       # i = i + 1 (adjusted for char* size)

# :collect_command_comment ; (0x80480A8)
    A1 6C820408                     ; mov_eax,[DWORD] &command_done     # Using command_done
    85C0                            ; test_eax,eax                      # IF 0 == command_done
    74 E9                           ; je !collect_command               # keep looping

    # Deal with line comments
    39CB                            ; cmp_ebx,ecx                       # if 0 < i
    74 CB                           ; je !main_loop                     # It was a comment

    E8 4F000000                     ; call %print_command               # print the command
    8B03                            ; mov_eax,[ebx]                     # program = tokens[0]
    85C0                            ; test_eax,eax                      # IF NULL == program
    74 18                           ; je !Exit_Failure                  # Some shit went down, abort

    53                              ; push_ebx                          # Protect Tokens
    6A 02                           ; push !2
    58                              ; pop_eax                           # FORKing
    CD 80                           ; int !0x80                         # int f = FORK()
    5B                              ; pop_ebx                           # Restore Tokens

    85C0                            ; test_eax,eax                      # Check fork
    7C 0D                           ; jl !Exit_Failure                  # IF f == -1 abort hard
    75 12                           ; jne !collect_command_parent       # IF f == 0 it is child

    # Deal with child case
    6A 0B                           ; push !11
    58                              ; pop_eax                           # EXECVE
    89EA                            ; mov_edx,ebp                       # third arg = envp
    89D9                            ; mov_ecx,ebx                       # second arg = tokens
    8B1B                            ; mov_ebx,[ebx]                     # program = tokens[0]
    CD 80                           ; int !0x80                         # execve(program, tokens, envp)
                                                                        # return error

# Exit_Failure function
# Receives nothing
# And aborts hard
# Does NOT return
# :Exit_Failure ; (0x80480D8)
    6A 01                           ; push !1
    5B                              ; pop_ebx                           # All is wrong
    89D8                            ; mov_eax,ebx                       # put the exit syscall number in eax
    CD 80                           ; int !0x80                         # Call it a bad day

# :collect_command_parent ; (0x80480DF)
    89C3                            ; mov_ebx,eax                       # first arg = f
    B9 7C820408                     ; mov_ecx, &status                  # second arg = &status
    31D2                            ; xor_edx,edx                       # third arg = NULL
    6A 07                           ; push !7
    58                              ; pop_eax                           # WAITPID
    CD 80                           ; int !0x80                         # waitpid(f, &status, 0)

    A1 7C820408                     ; mov_eax,[DWORD] &status           # Using status
    85C0                            ; test_eax,eax                      # IF 0 == status
    74 8A                           ; je !main_loop                     # Loop forever

    # Deal with very unhappy case
    B8 48820408                     ; mov_eax, &hard                    # Using "Subprocess error\nABORTING HARD\n"
    E8 07010000                     ; call %File_Print                  # Print it
    EB D6                           ; jmp !Exit_Failure                 # return error

# :Done ; (0x8048102)
    # program completed Successfully
    31DB                            ; xor_ebx,ebx                       # All is well
    6A 01                           ; push !1
    58                              ; pop_eax                           # put the exit syscall number in eax
    CD 80                           ; int !0x80                         # Call it a good day

# print_command function
# Receives tokens[j] in EBX and tokens[i] in ECX
# Modifies EAX
# :print_command ; (0x8048109)
    53                              ; push_ebx                          # Protect EBX
    B8 43820408                     ; mov_eax, &prefix                  # using " +> "
    E8 F3000000                     ; call %File_Print                  # print it
# :print_command_loop ; (0x8048114)
    8B03                            ; mov_eax,[ebx]                     # using tokens[j]
    E8 EC000000                     ; call %File_Print                  # print it
    83C3 04                         ; add_ebx, !4                       # j = j + 1
    6A 20                           ; push !32
    58                              ; pop_eax                           # using ' '
    E8 FE000000                     ; call %fputc                       # print it
    39CB                            ; cmp_ebx,ecx                       # IF j < i
    75 EA                           ; jne !print_command_loop           # otherwise keep looping

    6A 0A                           ; push !10
    58                              ; pop_eax                           # using '\n'
    E8 F2000000                     ; call %fputc                       # print it
    5B                              ; pop_ebx                           # Restore EBX
    C3                              ; ret


# collect_token function
# Receives nothing
# Overwrites EAX
# Uses EAX as C, EBX as token and ECX as token[i]
# :collect_token ; (0x8048134)
    53                              ; push_ebx                          # Protect EBX
    51                              ; push_ecx                          # Protect ECX
    A1 74820408                     ; mov_eax,[DWORD] &max_string       # Using max_string
    E8 89000000                     ; call %malloc                      # allocate space
    89C3                            ; mov_ebx,eax                       # token = malloc(max_string)
    89C1                            ; mov_ecx,eax                       # i = 0; set token[i]

# :collect_token_loop ; (0x8048144)
    E8 9F000000                     ; call %fgetc                       # c = fgetc(input)
    3C FC                           ; cmp_al, !-4                       # if C == EOF
    74 B5                           ; je !Done                          # We are done

    3C 20                           ; cmp_al, !32                       # IF C == ' '
    74 42                           ; je !collect_token_done            # Space terminates token

    3C 09                           ; cmp_al, !9                        # IF C == '\t'
    74 3E                           ; je !collect_token_done            # tab terminates token

    3C 0A                           ; cmp_al, !10                       # IF C == '\n'
    75 0A                           ; jne !collect_token_string         # otherwise check next

    # It is a newline
    6A 01                           ; push !1
    58                              ; pop_eax                           # Using 1
    A3 6C820408                     ; mov_[DWORD],eax &command_done     # Set command_done = TRUE
    EB 30                           ; jmp !collect_token_done           # Be done

# :collect_token_string ; (0x8048163)
    3C 22                           ; cmp_al, !34                       # IF C == '\"'
    75 07                           ; jne !collect_token_comment        # otherwise check next

    # It is a RAW STRING
    E8 32000000                     ; call %collect_string              # Get the rest of the string
    EB 25                           ; jmp !collect_token_done           # Be done

# :collect_token_comment ; (0x804816E)
    3C 23                           ; cmp_al, !35                       # IF C == '#'
    75 0F                           ; jne !collect_token_escape         # otherwise check next

    # It is a line comment
    E8 40000000                     ; call %collect_comment             # Read it all
    6A 01                           ; push !1
    58                              ; pop_eax                           # Using 1
    A3 6C820408                     ; mov_[DWORD],eax &command_done     # Set command_done = TRUE
    EB 12                           ; jmp !collect_token_done           # Be done

# :collect_token_escape ; (0x8048181)
    3C 5C                           ; cmp_al, !92                       # IF C == '\\'
    75 07                           ; jne !collect_token_other          # otherwise just store it

    # It is an escape char
    E8 5E000000                     ; call %fgetc                       # Read the char to drop
    EB 07                           ; jmp !collect_token_done           # Be done

# :collect_token_other ; (0x804818C)
    8801                            ; mov_[ecx],al                      # token[i] = C
    83C1 01                         ; add_ecx, !1                       # i = i + 1
    EB B1                           ; jmp !collect_token_loop           # Keep going

# :collect_token_done ; (0x8048193)
    39CB                            ; cmp_ebx,ecx                       # IF i == 0
    75 02                           ; jne !collect_token_good           # otherwise return the token
    31DB                            ; xor_ebx,ebx                       # token = NULL

# :collect_token_good ; (0x8048199)
    89D8                            ; mov_eax,ebx                       # Return token
    59                              ; pop_ecx                           # Restore ECX
    5B                              ; pop_ebx                           # Restore EBX
    C3                              ; ret


# collect_string function
# Receives target[index] in ECX
# Modifies EAX
# Uses EAX as C
# :collect_string ; (0x804819E)
    E8 45000000                     ; call %fgetc                       # C = fgetc(input)
    3C FC                           ; cmp_al, !-4                       # if C == EOF
    0F84 2DFFFFFF                   ; je32 %Exit_Failure                # Something went horribly wrong

    3C 22                           ; cmp_al, !34                       # IF C == '\"'
    74 07                           ; je !collect_string_done           # be done

    # deal with inside of string
    8801                            ; mov_[ecx],al                      # target[index] = C
    83C1 01                         ; add_ecx, !1                       # index = index + 1
    EB E8                           ; jmp !collect_string               # Keep going

# :collect_string_done ; (0x80481B6)
    C3                              ; ret


# collect_comment function
# Receives nothing
# Modifies EAX
# uses EAX as Int C
# Just throws away everything it reads
# :collect_comment ; (0x80481B7)
    E8 2C000000                     ; call %fgetc                       # C = fgetc(input)
    3C FC                           ; cmp_al, !-4                       # IF C == EOF
    0F84 14FFFFFF                   ; je32 %Exit_Failure                # abort hard

    3C 0A                           ; cmp_al, !10                       # IF C == '\n'
    75 EF                           ; jne !collect_comment              # otherwise keep looping
    C3                              ; ret


;; Malloc isn't actually required if the program being built fits in the initial memory
;; However, it doesn't take much to add it.
;; Requires [MALLOC] to be initialized and EAX to have the number of desired bytes
# :malloc ; (0x80481C9)
    53                              ; push_ebx                          # Protect EBX
    51                              ; push_ecx                          # Protect ECX
    52                              ; push_edx                          # Protect EDX
    8B1D 78820408                   ; mov_ebx,[DWORD] &MALLOC           # Using the current pointer
    01C3                            ; add_ebx,eax                       # Request the number of desired bytes
    6A 2D                           ; push !45
    58                              ; pop_eax                           # the Syscall # for SYS_BRK
    CD 80                           ; int !0x80                         # call the Kernel
    A1 78820408                     ; mov_eax,[DWORD] &MALLOC           # Return pointer
    891D 78820408                   ; mov_[DWORD],ebx &MALLOC           # Update pointer
    5A                              ; pop_edx                           # Restore EDX
    59                              ; pop_ecx                           # Restore ECX
    5B                              ; pop_ebx                           # Restore EBX
    C3                              ; ret


# fgetc function
# Loads FILE* from [script]
# Returns -4 (EOF) or char in AL
# :fgetc ; (0x80481E8)
    53                              ; push_ebx                          # Protect EBX
    51                              ; push_ecx                          # Protect ECX
    52                              ; push_edx                          # Protect EDX
    6A FC                           ; push !-4
    58                              ; pop_eax                           # Put EOF in eax
    50                              ; push_eax                          # Assume bad (If nothing read, value will remain EOF)
    8D0C24                          ; lea_ecx,[esp]                     # Get stack address
    8B1D 68820408                   ; mov_ebx,[DWORD] &script           # Where are we reading from
    6A 03                           ; push !3
    58                              ; pop_eax                           # the syscall number for read
    6A 01                           ; push !1
    5A                              ; pop_edx                           # set the size of chars we want
    CD 80                           ; int !0x80                         # call the Kernel
    58                              ; pop_eax                           # Get either char or EOF
    3C FC                           ; cmp_al, !-4                       # Check for EOF
# :fgetc_done ; (0x8048203)
    5A                              ; pop_edx                           # Restore EDX
    59                              ; pop_ecx                           # Restore ECX
    5B                              ; pop_ebx                           # Restore EBX
    C3                              ; ret


# File_Print function
# Receives CHAR* in EAX
# calls fputc for every non-null char
# :File_Print ; (0x8048207)
    53                              ; push_ebx                          # Protect EBX
    51                              ; push_ecx                          # Protect ECX
    89C3                            ; mov_ebx,eax                       # Protect S
    85C0                            ; test_eax,eax                      # Protect against nulls
    74 12                           ; je !File_Print_Done               # Simply don't try to print them
# :File_Print_Loop ; (0x804820F)
    31C0                            ; xor_eax,eax                       # Zero eax
    8A03                            ; mov_al,[ebx]                      # Read byte
    85C0                            ; test_eax,eax                      # Check for NULL
    74 0A                           ; je !File_Print_Done               # Stop at NULL

    E8 08000000                     ; call %fputc                       # write it
    83C3 01                         ; add_ebx, !1                       # S = S + 1
    EB EE                           ; jmp !File_Print_Loop              # Keep going

# :File_Print_Done ; (0x8048221)
    59                              ; pop_ecx                           # Restore ECX
    5B                              ; pop_ebx                           # Restore EBX
    C3                              ; ret


# fputc function
# receives CHAR in EAX and load FILE* from stdout
# writes char and returns
# :fputc ; (0x8048224)
    53                              ; push_ebx                          # Protect EBX
    51                              ; push_ecx                          # Protect ECX
    52                              ; push_edx                          # Protect EDX
    50                              ; push_eax                          # We are writing eax
    8D0C24                          ; lea_ecx,[esp]                     # Get stack address
    6A 01                           ; push !1
    5B                              ; pop_ebx                           # Write to target file
    6A 04                           ; push !4
    58                              ; pop_eax                           # the syscall number for write
    89DA                            ; mov_edx,ebx                       # set the size of chars we want
    CD 80                           ; int !0x80                         # call the Kernel
    58                              ; pop_eax                           # Restore stack
    5A                              ; pop_edx                           # Restore EDX
    59                              ; pop_ecx                           # Restore ECX
    5B                              ; pop_ebx                           # Restore EBX
    C3                              ; ret


# :default_file ; (0x804823A)
    6B61656D2E78383600              # "kaem.x86"
# :prefix ; (0x8048243)
    202B3E2000                      # " +> "
# :hard ; (0x8048248)
    53756270726F63657373206572726F720A41424F5254494E4720484152440A00 # "Subprocess error\nABORTING HARD\n"
# :script ; (0x8048268)
    00000000
# :command_done ; (0x804826C)
    00000000
# :max_args ; (0x8048270)
    00040000
# :max_string ; (0x8048274)
    00100000
# :MALLOC ; (0x8048278)
    00000000
# :status ; (0x804827C)
    00000000
# :ELF_end ; (0x8048280)

File /x86/mescc-tools-mini-kaem.kaem

Live-bootstrap source file is 'seed/stage0-posix/x86/mescc-tools-mini-kaem.kaem'.
URL: https://github.com/oriansj/stage0-posix-x86/blob/83508012c952e3aa3f70a89fd7d85d9a1af6f305/mescc-tools-mini-kaem.kaem
#! /usr/bin/env bash
# Mes --- Maxwell Equations of Software
# Copyright © 2017,2019 Jan Nieuwenhuizen <janneke@gnu.org>
# Copyright © 2017,2019 Jeremiah Orians
#
# This file is part of Mes.
#
# Mes is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or (at
# your option) any later version.
#
# Mes is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Mes.  If not, see <http://www.gnu.org/licenses/>.



# Can also be run by kaem or any other shell of your personal choice
# To run in kaem simply: kaem --verbose --strict
# Warning all binaries prior to the use of blood-elf will not be readable by
# Objdump, you may need to use ndism or gdb to view the assembly in the binary.


#################################
# Phase-1 Build hex1 from hex0  #
#################################
./x86/artifact/hex0 ./x86/hex1_x86.hex0 ./x86/artifact/hex1
# hex1 adds support for single character labels and is available in various forms
# in mescc-tools/x86_bootstrap to allow you various ways to verify correctness

#################################
# Phase-2 Build hex2 from hex1  #
#################################
./x86/artifact/hex1 ./x86/hex2_x86.hex1 ./x86/artifact/hex2-0
# hex2 adds support for long labels and absolute addresses thus allowing it
# to function as an effective linker for later stages of the bootstrap
# This is a minimal version which will be used to bootstrap a much more advanced
# version in a later stage.

#################################
# Phase-2b Build catm from hex2 #
#################################
./x86/artifact/hex2-0 ./x86/catm_x86.hex2 ./x86/artifact/catm
# catm removes the need for cat or shell support for redirection by providing
# equivalent functionality via catm output_file input1 input2 ... inputN

###############################
# Phase-3 Build M0 from hex2  #
###############################
./x86/artifact/catm ./x86/artifact/M0.hex2 ./x86/ELF-i386.hex2 ./x86/M0_x86.hex2
./x86/artifact/hex2-0 ./x86/artifact/M0.hex2 ./x86/artifact/M0
# M0 is the architecture specific version of M1 and is by design single
# architecture only and will be replaced by the C code version of M1

###################################
# Phase-4 Build cc_x86 from M0    #
###################################
./x86/artifact/M0 ./x86/cc_x86.M1 ./x86/artifact/cc_x86-0.hex2
./x86/artifact/catm ./x86/artifact/cc_x86-1.hex2 ./x86/ELF-i386.hex2 ./x86/artifact/cc_x86-0.hex2
./x86/artifact/hex2-0 ./x86/artifact/cc_x86-1.hex2 ./x86/artifact/cc_x86


#########################################
# Phase-5 Build M2-Planet from cc_x86   #
#########################################
./x86/artifact/catm ./x86/artifact/M2-0.c \
    ./M2libc/x86/linux/bootstrap.c \
    ./M2-Planet/cc.h \
    ./M2libc/bootstrappable.c \
    ./M2-Planet/cc_globals.c \
    ./M2-Planet/cc_reader.c \
    ./M2-Planet/cc_strings.c \
    ./M2-Planet/cc_types.c \
    ./M2-Planet/cc_core.c \
    ./M2-Planet/cc_macro.c \
    ./M2-Planet/cc.c
./x86/artifact/cc_x86 ./x86/artifact/M2-0.c ./x86/artifact/M2-0.M1
./x86/artifact/catm ./x86/artifact/M2-0-0.M1 ./x86/x86_defs.M1 ./x86/libc-core.M1 ./x86/artifact/M2-0.M1
./x86/artifact/M0 ./x86/artifact/M2-0-0.M1 ./x86/artifact/M2-0.hex2
./x86/artifact/catm ./x86/artifact/M2-0-0.hex2 ./x86/ELF-i386.hex2 ./x86/artifact/M2-0.hex2
./x86/artifact/hex2-0 ./x86/artifact/M2-0-0.hex2 ./x86/artifact/M2

#############################################
# Phase-6 Build blood-elf-0 from C sources  #
#############################################
./x86/artifact/M2 --architecture x86 \
    -f ./M2libc/x86/linux/bootstrap.c \
    -f ./M2libc/bootstrappable.c \
    -f ./mescc-tools/stringify.c \
    -f ./mescc-tools/blood-elf.c \
    --bootstrap-mode \
    -o ./x86/artifact/blood-elf-0.M1

./x86/artifact/catm ./x86/artifact/blood-elf-0-0.M1 ./M2libc/x86/x86_defs.M1  ./M2libc/x86/libc-core.M1 ./x86/artifact/blood-elf-0.M1
./x86/artifact/M0 ./x86/artifact/blood-elf-0-0.M1 ./x86/artifact/blood-elf-0.hex2
./x86/artifact/catm ./x86/artifact/blood-elf-0-0.hex2 ./M2libc/x86/ELF-x86.hex2 ./x86/artifact/blood-elf-0.hex2
./x86/artifact/hex2-0 ./x86/artifact/blood-elf-0-0.hex2 ./x86/artifact/blood-elf-0
# This is the last stage where the binaries will not have debug info
# and the last piece built that isn't part of the output binaries

#####################################
# Phase-7 Build M1-0 from C sources #
#####################################
./x86/artifact/M2 --architecture x86 \
    -f ./M2libc/x86/linux/bootstrap.c \
    -f ./M2libc/bootstrappable.c \
    -f ./mescc-tools/stringify.c \
    -f ./mescc-tools/M1-macro.c \
    --bootstrap-mode \
    --debug \
    -o ./x86/artifact/M1-macro-0.M1

./x86/artifact/blood-elf-0 -f ./x86/artifact/M1-macro-0.M1 --little-endian -o ./x86/artifact/M1-macro-0-footer.M1
./x86/artifact/catm ./x86/artifact/M1-macro-0-0.M1 ./M2libc/x86/x86_defs.M1  ./M2libc/x86/libc-core.M1 ./x86/artifact/M1-macro-0.M1 ./x86/artifact/M1-macro-0-footer.M1
./x86/artifact/M0 ./x86/artifact/M1-macro-0-0.M1 ./x86/artifact/M1-macro-0.hex2
./x86/artifact/catm ./x86/artifact/M1-macro-0-0.hex2 ./M2libc/x86/ELF-x86-debug.hex2 ./x86/artifact/M1-macro-0.hex2
./x86/artifact/hex2-0 ./x86/artifact/M1-macro-0-0.hex2 ./x86/artifact/M1-0

# This is the last stage where catm will need to be used and the last stage where
# M0 is used, as we will being using its much more powerful and cross-platform
# version with a bunch of extra goodies.

#######################################
# Phase-8 Build hex2-1 from C sources #
#######################################
./x86/artifact/M2 --architecture x86 \
    -f ./M2libc/sys/types.h \
    -f ./M2libc/stddef.h \
    -f ./M2libc/sys/utsname.h \
    -f ./M2libc/x86/linux/unistd.c \
    -f ./M2libc/x86/linux/fcntl.c \
    -f ./M2libc/fcntl.c \
    -f ./M2libc/x86/linux/sys/stat.c \
    -f ./M2libc/stdlib.c \
    -f ./M2libc/stdio.h \
    -f ./M2libc/stdio.c \
    -f ./M2libc/bootstrappable.c \
    -f ./mescc-tools/hex2.h \
    -f ./mescc-tools/hex2_linker.c \
    -f ./mescc-tools/hex2_word.c \
    -f ./mescc-tools/hex2.c \
    --debug \
    -o ./x86/artifact/hex2_linker-0.M1

./x86/artifact/blood-elf-0 -f ./x86/artifact/hex2_linker-0.M1 --little-endian -o ./x86/artifact/hex2_linker-0-footer.M1

./x86/artifact/M1-0 --architecture x86 \
    --little-endian \
    -f ./M2libc/x86/x86_defs.M1 \
    -f ./M2libc/x86/libc-full.M1 \
    -f ./x86/artifact/hex2_linker-0.M1 \
    -f ./x86/artifact/hex2_linker-0-footer.M1 \
    -o ./x86/artifact/hex2_linker-0.hex2

./x86/artifact/catm ./x86/artifact/hex2_linker-0-0.hex2 ./M2libc/x86/ELF-x86-debug.hex2 ./x86/artifact/hex2_linker-0.hex2

./x86/artifact/hex2-0 ./x86/artifact/hex2_linker-0-0.hex2 ./x86/artifact/hex2-1

# This is the last stage where we will be using the handwritten hex2 and instead
# be using the far more powerful, cross-platform version with a bunch more goodies

###################################
# Phase-9 Build M1 from C sources #
###################################
./x86/artifact/M2 --architecture x86 \
    -f ./M2libc/sys/types.h \
    -f ./M2libc/stddef.h \
    -f ./M2libc/x86/linux/fcntl.c \
    -f ./M2libc/fcntl.c \
    -f ./M2libc/sys/utsname.h \
    -f ./M2libc/x86/linux/unistd.c \
    -f ./M2libc/string.c \
    -f ./M2libc/stdlib.c \
    -f ./M2libc/stdio.h \
    -f ./M2libc/stdio.c \
    -f ./M2libc/bootstrappable.c \
    -f ./mescc-tools/stringify.c \
    -f ./mescc-tools/M1-macro.c \
    --debug \
    -o ./x86/artifact/M1-macro-1.M1

./x86/artifact/blood-elf-0 -f ./x86/artifact/M1-macro-1.M1 --little-endian -o ./x86/artifact/M1-macro-1-footer.M1

./x86/artifact/M1-0 --architecture x86 \
    --little-endian \
    -f ./M2libc/x86/x86_defs.M1 \
    -f ./M2libc/x86/libc-full.M1 \
    -f ./x86/artifact/M1-macro-1.M1 \
    -f ./x86/artifact/M1-macro-1-footer.M1 \
    -o ./x86/artifact/M1-macro-1.hex2

./x86/artifact/hex2-1 --architecture x86 \
    --little-endian \
    --base-address 0x8048000 \
    -f ./M2libc/x86/ELF-x86-debug.hex2 \
    -f ./x86/artifact/M1-macro-1.hex2 \
    -o ./x86/bin/M1

######################################
# Phase-10 Build hex2 from C sources #
######################################
./x86/artifact/M2 --architecture x86 \
    -f ./M2libc/sys/types.h \
    -f ./M2libc/stddef.h \
    -f ./M2libc/sys/utsname.h \
    -f ./M2libc/x86/linux/unistd.c \
    -f ./M2libc/x86/linux/fcntl.c \
    -f ./M2libc/fcntl.c \
    -f ./M2libc/x86/linux/sys/stat.c \
    -f ./M2libc/stdlib.c \
    -f ./M2libc/stdio.h \
    -f ./M2libc/stdio.c \
    -f ./M2libc/bootstrappable.c \
    -f ./mescc-tools/hex2.h \
    -f ./mescc-tools/hex2_linker.c \
    -f ./mescc-tools/hex2_word.c \
    -f ./mescc-tools/hex2.c \
    --debug \
    -o ./x86/artifact/hex2_linker-2.M1

./x86/artifact/blood-elf-0 -f ./x86/artifact/hex2_linker-2.M1 --little-endian -o ./x86/artifact/hex2_linker-2-footer.M1

./x86/bin/M1 --architecture x86 \
    --little-endian \
    -f ./M2libc/x86/x86_defs.M1 \
    -f ./M2libc/x86/libc-full.M1 \
    -f ./x86/artifact/hex2_linker-2.M1 \
    -f ./x86/artifact/hex2_linker-2-footer.M1 \
    -o ./x86/artifact/hex2_linker-2.hex2

./x86/artifact/hex2-1 --architecture x86 \
    --little-endian \
    --base-address 0x8048000 \
    -f ./M2libc/x86/ELF-x86-debug.hex2 \
    -f ./x86/artifact/hex2_linker-2.hex2 \
    -o ./x86/bin/hex2


#####################################
# Phase-11 Build kaem from C sources#
#####################################
./x86/artifact/M2 --architecture x86 \
    -f ./M2libc/sys/types.h \
    -f ./M2libc/stddef.h \
    -f ./M2libc/string.c \
    -f ./M2libc/sys/utsname.h \
    -f ./M2libc/x86/linux/unistd.c \
    -f ./M2libc/x86/linux/fcntl.c \
    -f ./M2libc/fcntl.c \
    -f ./M2libc/stdlib.c \
    -f ./M2libc/stdio.h \
    -f ./M2libc/stdio.c \
    -f ./M2libc/bootstrappable.c \
    -f ./mescc-tools/Kaem/kaem.h \
    -f ./mescc-tools/Kaem/variable.c \
    -f ./mescc-tools/Kaem/kaem_globals.c \
    -f ./mescc-tools/Kaem/kaem.c \
    --debug \
    -o ./x86/artifact/kaem.M1

./x86/artifact/blood-elf-0 -f ./x86/artifact/kaem.M1 --little-endian -o ./x86/artifact/kaem-footer.M1

./x86/bin/M1 --architecture x86 \
    --little-endian \
    -f ./M2libc/x86/x86_defs.M1 \
    -f ./M2libc/x86/libc-full.M1 \
    -f ./x86/artifact/kaem.M1 \
    -f ./x86/artifact/kaem-footer.M1 \
    -o ./x86/artifact/kaem.hex2

./x86/bin/hex2 --architecture x86 \
    --little-endian \
    -f ./M2libc/x86/ELF-x86-debug.hex2 \
    -f ./x86/artifact/kaem.hex2 \
    --base-address 0x8048000 \
    -o ./x86/bin/kaem

File /x86/hex1_x86.hex0

Live-bootstrap source file is 'seed/stage0-posix/x86/hex1_x86.hex0'.
URL: https://github.com/oriansj/stage0-posix-x86/blob/83508012c952e3aa3f70a89fd7d85d9a1af6f305/hex1_x86.hex0
# SPDX-FileCopyrightText: © 2017 Jeremiah Orians
#
# SPDX-License-Identifier: GPL-3.0-or-later

## ELF Header
#:ELF_base
7F 45 4C 46                     # e_ident[EI_MAG0-3] ELF's magic number

01                              # e_ident[EI_CLASS] Indicating 32 bit
01                              # e_ident[EI_DATA] Indicating little endianness
01                              # e_ident[EI_VERSION] Indicating original elf

03                              # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                              # e_ident[EI_ABIVERSION] Set at 0 because none cares

00 00 00 00 00 00 00            # e_ident[EI_PAD]

02 00                           # e_type Indicating Executable
03 00                           # e_machine Indicating x86
01 00 00 00                     # e_version Indicating original elf

54 80 04 08                     # e_entry Address of the entry point
34 00 00 00                     # e_phoff Address of program header table
00 00 00 00                     # e_shoff Address of section header table

00 00 00 00                     # e_flags

34 00                           # e_ehsize Indicating our 52 Byte header

20 00                           # e_phentsize size of a program header table
01 00                           # e_phnum number of entries in program table

00 00                           # e_shentsize size of a section header table
00 00                           # e_shnum number of entries in section table

00 00                           # e_shstrndx index of the section names

## Program Header
#:ELF_program_headers
#:ELF_program_header__text
01 00 00 00                     # ph_type: PT-LOAD = 1
00 00 00 00                     # ph_offset

00 80 04 08                     # ph_vaddr
00 80 04 08                     # ph_physaddr

B1 02 00 00                     # ph_filesz
B1 02 00 00                     # ph_memsz

07 00 00 00                     # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                     # ph_align

#:ELF_text

#:_start ; (0x8048054)
    58                          ; pop_eax                     # Get the number of arguments
    5B                          ; pop_ebx                     # Get the program name
    5B                          ; pop_ebx                     # Get the actual input name
    B9 00000000                 ; mov_ecx, %0                 # prepare read_only
    BA 00000000                 ; mov_edx, %0                 # extra sure
    B8 05000000                 ; mov_eax, %5                 # the syscall number for open()
    CD 80                       ; int !0x80                   # Now open that damn file
    A3 A9820408                 ; mov_[DWORD],eax &fin        # Preserve the file pointer we were given

    5B                          ; pop_ebx                     # Get the actual output name
    B9 41020000                 ; mov_ecx, %577               # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    BA C0010000                 ; mov_edx, %448               # Prepare file as RWX for owner only (700 in octal)
    B8 05000000                 ; mov_eax, %5                 # the syscall number for open()
    CD 80                       ; int !0x80                   # Now open that damn file
    A3 AD820408                 ; mov_[DWORD],eax &fout       # Preserve the file pointer we were given

    BD FFFFFFFF                 ; mov_ebp, %-1                # Our flag for byte processing
    BE 00000000                 ; mov_esi, %0                 # temp storage for the sum
    BF 00000000                 ; mov_edi, %0                 # Our starting IP
    E8 30000000                 ; call %First_pass            # Process it

    # rewind input file
    8B1D A9820408               ; mov_ebx,[DWORD] &fin        # Using our input file
    B9 00000000                 ; mov_ecx, %0                 # Offset Zero
    BA 00000000                 ; mov_edx, %0                 # Whence Zero
    B8 13000000                 ; mov_eax, %19                # lseek
    CD 80                       ; int !0x80

    BD FFFFFFFF                 ; mov_ebp, %-1                # Our flag for byte processing
    BE 00000000                 ; mov_esi, %0                 # temp storage for the sum
    BF 00000000                 ; mov_edi, %0                 # Our starting IP
    E8 B8000000                 ; call %Second_pass           # Process it

    E9 62010000                 ; jmp %Done

#:First_pass ; (0x80480C8)
    E8 69010000                 ; call %Read_byte

    # Deal with EOF
    83F8 FC                     ; cmp_eax, !-4
    0F84 4E000000               ; je %First_pass_done

    # Check for :
    83F8 3A                     ; cmp_eax, !58
    0F85 05000000               ; jne %First_pass_0

    # Deal with label
    E8 A1010000                 ; call %StoreLabel

#:First_pass_0 ; (0x80480E4)
    # Check for %
    83F8 25                     ; cmp_eax, !37
    0F84 2A000000               ; je %First_pass_pointer

    # Deal with everything else
    E8 33000000                 ; call %hex                   # Process our char

    # Deal with EOF
    83F8 FC                     ; cmp_eax, !-4
    0F84 29000000               ; je %First_pass_done

    # deal with -1 values
    83F8 00                     ; cmp_eax, !0
    0F8C C4FFFFFF               ; jl %First_pass

    # deal with toggle
    83FD 00                     ; cmp_ebp, !0
    0F84 03000000               ; je %First_pass_1
    83C7 01                     ; add_edi, !1                 # Increment IP

#:First_pass_1 ; (0x8048110)
    F7D5                        ; not_ebp
    E9 B1FFFFFF                 ; jmp %First_pass

#:First_pass_pointer ; (0x8048117)
    # Deal with Pointer to label
    E8 1A010000                 ; call %Read_byte             # Drop the char
    83C7 04                     ; add_edi, !4                 # Increment IP
    E9 A4FFFFFF                 ; jmp %First_pass             # Loop again

#:First_pass_done ; (0x8048124)
    C3                          ; ret

#:hex ; (0x8048125)
    # deal with EOF
    83F8 FC                     ; cmp_eax, !-4
    0F84 AE000000               ; je %EOF
    # deal with line comments starting with #
    83F8 23                     ; cmp_eax, !35
    0F84 B8000000               ; je %ascii_comment
    # deal with line comments starting with ;
    83F8 3B                     ; cmp_eax, !59
    0F84 AF000000               ; je %ascii_comment
    # deal all ascii less than 0
    83F8 30                     ; cmp_eax, !48
    0F8C A0000000               ; jl %ascii_other
    # deal with 0-9
    83F8 3A                     ; cmp_eax, !58
    0F8C 8B000000               ; jl %ascii_num
    # deal with all ascii less than A
    83F8 41                     ; cmp_eax, !65
    0F8C 8E000000               ; jl %ascii_other
    # deal with A-F
    83F8 47                     ; cmp_eax, !71
    0F8C 81000000               ; jl %ascii_high
    # deal with all ascii less than a
    83F8 61                     ; cmp_eax, !97
    0F8C 7C000000               ; jl %ascii_other
    # deal with a-f
    83F8 67                     ; cmp_eax, !103
    0F8C 6B000000               ; jl %ascii_low
    # The rest that remains needs to be ignored
    E9 6E000000                 ; jmp %ascii_other

#:Second_pass ; (0x804817B)
    E8 B6000000                 ; call %Read_byte

    # Deal with EOF
    83F8 FC                     ; cmp_eax, !-4
    0F84 52000000               ; je %Second_pass_done

    # Simply drop the label
    83F8 3A                     ; cmp_eax, !58
    0F85 0A000000               ; jne %Second_pass_0

    E8 9F000000                 ; call %Read_byte
    E9 DFFFFFFF                 ; jmp %Second_pass

#:Second_pass_0 ; (0x804819C)
    # Deal with  pointer
    83F8 25                     ; cmp_eax, !37
    0F85 0A000000               ; jne %Second_pass_1

    E8 E3000000                 ; call %StorePointer
    E9 CCFFFFFF                 ; jmp %Second_pass

#:Second_pass_1 ; (0x80481AF)
    # Deal with everything else
    E8 71FFFFFF                 ; call %hex                   # Process our char

    # Deal with EOF
    83F8 FC                     ; cmp_eax, !-4
    0F84 1E000000               ; je %Second_pass_done

    # deal with -1 values
    83F8 00                     ; cmp_eax, !0
    0F8C B5FFFFFF               ; jl %Second_pass

    # deal with toggle
    83FD 00                     ; cmp_ebp, !0
    0F84 3D000000               ; je %print

    # process first byte of pair
    89C6                        ; mov_esi,eax
    BD 00000000                 ; mov_ebp, %0
    E9 A0FFFFFF                 ; jmp %Second_pass

#:Second_pass_done ; (0x80481DB)
    C3                          ; ret

#:EOF ; (0x80481DC)
    C3                          ; ret
#:ascii_num ; (0x80481DD)
    83E8 30                     ; sub_eax, !48
    C3                          ; ret
#:ascii_low ; (0x80481E1)
    83E8 57                     ; sub_eax, !87
    C3                          ; ret
#:ascii_high ; (0x80481E5)
    83E8 37                     ; sub_eax, !55
    C3                          ; ret
#:ascii_other ; (0x80481E9)
    B8 FFFFFFFF                 ; mov_eax, %-1
    C3                          ; ret
#:ascii_comment ; (0x80481EF)
    E8 42000000                 ; call %Read_byte
    83F8 0D                     ; cmp_eax, !13
    0F84 09000000               ; je %ascii_comment_cr
    83F8 0A                     ; cmp_eax, !10
    0F85 E9FFFFFF               ; jne %ascii_comment
#:ascii_comment_cr ; (0x8048206)
    B8 FFFFFFFF                 ; mov_eax, %-1
    C3                          ; ret

# process second byte of pair
#:print ; (0x804820C)
    # update the sum and store in output
    C1E6 04                     ; shl_esi, !4
    01F0                        ; add_eax,esi
    A2 B1820408                 ; mov_[DWORD],al &table

    # flip the toggle
    F7D5                        ; not_ebp

    # Print our first Hex
    BA 01000000                 ; mov_edx, %1                 # set the size of chars we want
    E8 42000000                 ; call %print_chars

    83C7 01                     ; add_edi, !1                 # Increment IP
    E9 51FFFFFF                 ; jmp %Second_pass

#:Done ; (0x804822A)
    # program completed Successfully
    BB 00000000                 ; mov_ebx, %0                 # All is well
    B8 01000000                 ; mov_eax, %1                 # put the exit syscall number in eax
    CD 80                       ; int !0x80                   # Call it a good day

#:Read_byte ; (0x8048236)
    # Attempt to read 1 byte from STDIN
    BA 01000000                 ; mov_edx, %1                 # set the size of chars we want
    B9 B1820408                 ; mov_ecx, &table             # Where to put it
    8B1D A9820408               ; mov_ebx,[DWORD] &fin        # Where are we reading from
    B8 03000000                 ; mov_eax, %3                 # the syscall number for read
    CD 80                       ; int !0x80                   # call the Kernel

    85C0                        ; test_eax,eax                # check what we got
    0F84 09000000               ; je %Read_byte_1             # Got EOF call it done

    # load byte
    A0 B1820408                 ; mov_al,[DWORD] &table       # load char
    0FB6C0                      ; movzx_eax,al                # We have to zero extend it to use it
    C3                          ; ret

# Deal with EOF
#:Read_byte_1 ; (0x804825E)
    B8 FCFFFFFF                 ; mov_eax, %-4                # Put EOF in eax
    C3                          ; ret

#:print_chars ; (0x8048264)
    B9 B1820408                 ; mov_ecx, &table             # What we are writing
    8B1D AD820408               ; mov_ebx,[DWORD] &fout       # Write to target file
    B8 04000000                 ; mov_eax, %4                 # the syscall number for write
    CD80                        ; int !0x80                   # call the Kernel
    C3                          ; ret

#:Get_table_target ; (0x8048277)
    E8 BAFFFFFF                 ; call %Read_byte             # Get single char label
    C1E0 02                     ; shl_eax, !2                 # Each label in table takes 4 bytes to store
    05 B1820408                 ; add_eax, &table             # Calculate offset
    C3                          ; ret

#:StoreLabel ; (0x8048285)
    E8 EDFFFFFF                 ; call %Get_table_target
    8938                        ; mov_[eax],edi               # Write out pointer to table
    C3                          ; ret

#:StorePointer ; (0x804828D)
    83C7 04                     ; add_edi, !4                 # Increment IP
    E8 E2FFFFFF                 ; call %Get_table_target      # Get address of pointer
    8B00                        ; mov_eax,[eax]               # Get pointer
    29F8                        ; sub_eax,edi                 # target - ip
    A3 B1820408                 ; mov_[DWORD],eax &table      # put value in output
    BA 04000000                 ; mov_edx, %4                 # set the size of chars we want
    E8 BCFFFFFF                 ; call %print_chars
    C3                          ; ret

#:fin ; (0x80482A9)
    00000000                    ; %0
#:fout ; (0x80482AD)
    00000000                    ; %0
#:table ; (0x80482B1)
#:ELF_end

File /x86/hex2_x86.hex1

Live-bootstrap source file is 'seed/stage0-posix/x86/hex2_x86.hex1'.
URL: https://github.com/oriansj/stage0-posix-x86/blob/83508012c952e3aa3f70a89fd7d85d9a1af6f305/hex2_x86.hex1
# SPDX-FileCopyrightText: © 2017 Jeremiah Orians
#
# SPDX-License-Identifier: GPL-3.0-or-later

## ELF Header
7F 45 4C 46                     # e_ident[EI_MAG0-3] ELF's magic number

01                              # e_ident[EI_CLASS] Indicating 32 bit
01                              # e_ident[EI_DATA] Indicating little endianness
01                              # e_ident[EI_VERSION] Indicating original elf

03                              # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                              # e_ident[EI_ABIVERSION] Set at 0 because none cares

00 00 00 00 00 00 00            # e_ident[EI_PAD]

02 00                           # e_type Indicating Executable
03 00                           # e_machine Indicating x86
01 00 00 00                     # e_version Indicating original elf

54 80 04 08                     # e_entry Address of the entry point
34 00 00 00                     # e_phoff Address of program header table
00 00 00 00                     # e_shoff Address of section header table

00 00 00 00                     # e_flags

34 00                           # e_ehsize Indicating our 52 Byte header

20 00                           # e_phentsize size of a program header table
01 00                           # e_phnum number of entries in program table

00 00                           # e_shentsize size of a section header table
00 00                           # e_shnum number of entries in section table

00 00                           # e_shstrndx index of the section names

## Program Header
01 00 00 00                     # ph_type: PT-LOAD = 1
00 00 00 00                     # ph_offset

00 80 04 08                     # ph_vaddr
00 80 04 08                     # ph_physaddr

7D 05 00 00                     # ph_filesz
7D 05 00 00                     # ph_memsz

07 00 00 00                    # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                    # ph_align

    # Register usage:
    # EAX, EDX, ECX, EBX => Temps
    # EDI => IP
    # EBP => MALLOC
    # ESI => HEAD

    # Struct format: (size 24)
    # NEXT => 0
    # TARGET => 8
    # NAME => 16

#:_start
    BB 00000000                 ; mov_ebx, %0                 # Get current pointer
    E8 %x                       ; call %malloc                # Get current HEAP
    89C3                        ; mov_ebx,eax                 # Using current
    89C5                        ; mov_ebp,eax                 # Setup MALLOC
    81C3 0000C000               ; addd_ebx, %12582912         # Create space for temp [12M]
    E8 %x                       ; call %malloc                # Give ourselves 8192000 bytes to work with

    58                          ; pop_eax                     # Get the number of arguments
    5B                          ; pop_ebx                     # Get the program name
    5B                          ; pop_ebx                     # Get the actual input name
    B9 00000000                 ; mov_ecx, %0                 # prepare read_only
    BA 00000000                 ; mov_edx, %0                 # Really sure
    B8 05000000                 ; mov_eax, %5                 # the syscall number for open()
    CD 80                       ; int !0x80                   # Now open that damn file
    A3 6D850408                 ; mov_[DWORD],eax &Input      # Preserve the file pointer we were given

    5B                          ; pop_ebx                     # Get the actual output name
    B9 41020000                 ; mov_ecx, %577               # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    BA C0010000                 ; mov_edx, %448               # Prepare file as RWX for owner only (700 in octal)
    B8 05000000                 ; mov_eax, %5                 # the syscall number for open()
    CD 80                       ; int !0x80                   # Now open that damn file
    83F8 00                     ; cmp_eax, !0                 # Check for missing output
    0F8F %a                     ; jg %_start_out              # Have real input
    B8 01000000                 ; mov_eax, %1                 # Use stdout

:a #:_start_out
    A3 71850408                 ; mov_[DWORD],eax &Output     # Preserve the file pointer we were given


    E8 %I                       ; call %ClearScratch          # Zero scratch
    B8 FFFFFFFF                 ; mov_eax, %-1                # Our flag for byte processing
    A3 69850408                 ; mov_[DWORD],eax &Flag       # Set
    B8 00000000                 ; mov_eax, %0                 # temp storage for the sum
    A3 65850408                 ; mov_[DWORD],eax &High       # Set
    BF 00800408                 ; mov_edi, %0x8048000         # Our starting IP
    BE 00000000                 ; mov_esi, %0                 # HEAD = NULL
    E8 %b                       ; call %First_pass            # Process it

    # rewind input file
    8B1D 6D850408               ; mov_ebx,[DWORD] &Input      # Using our input file
    B9 00000000                 ; mov_ecx, %0                 # Offset Zero
    BA 00000000                 ; mov_edx, %0                 # Whence Zero
    B8 13000000                 ; mov_eax, %19                # lseek
    56                          ; push_esi                    # Protect HEAD
    CD 80                       ; int !0x80
    5E                          ; pop_esi                     # Restore HEAD

    B8 FFFFFFFF                 ; mov_eax, %-1                # Our flag for byte processing
    A3 69850408                 ; mov_[DWORD],eax &Flag       # Set
    B8 00000000                 ; mov_eax, %0                 # temp storage for the sum
    A3 65850408                 ; mov_[DWORD],eax &High       # Set
    BF 00800408                 ; mov_edi, %0x8048000         # Our starting IP
    E8 %l                       ; call %Second_pass           # Process it

    E9 %w                       ; jmp %Done

:b #:First_pass
    E8 %y                       ; call %Read_byte

    # Deal with EOF
    83F8 FC                     ; cmp_eax, !-4
    0F84 %j                     ; je %First_pass_done

    # Check for :
    83F8 3A                     ; cmp_eax, !0x3A
    0F85 %c                     ; jne %First_pass_0

    # Deal with label
    E9 %D                       ; jmp %StoreLabel

:c #:First_pass_0
    # Check for !
    83F8 21                     ; cmp_eax, !0x21
    0F84 %i                     ; je %First_pass_pointer

    # Check for @
    83F8 40                     ; cmp_eax, !0x40
    0F84 %i                     ; je %First_pass_pointer

    # Check for $
    83F8 24                     ; cmp_eax, !0x24
    0F84 %i                     ; je %First_pass_pointer

    # Check for %
    83F8 25                     ; cmp_eax, !0x25
    0F84 %i                     ; je %First_pass_pointer

    # Check for &
    83F8 26                     ; cmp_eax, !0x26
    0F84 %i                     ; je %First_pass_pointer

    # Deal with everything else
    E8 %k                       ; call %hex                   # Process our char

    # Deal with EOF
    83F8 FC                     ; cmp_eax, !-4
    0F84 %j                     ; je %First_pass_done

    # deal with -1 values
    83F8 00                     ; cmp_eax, !0
    0F8C %b                     ; jl %First_pass

    # deal with toggle
    A1 69850408                 ; mov_eax,[DWORD] &Flag
    83F8 00                     ; cmp_eax, !0
    0F84 %d                     ; je %First_pass_1
    83C7 01                     ; add_edi, !1                 # Increment IP

:d #:First_pass_1
    F7D0                        ; not_eax
    A3 69850408                 ; mov_[DWORD],eax &Flag
    E9 %b                       ; jmp %First_pass

:e #:Update_Pointer
    # Check for !
    83F8 21                     ; cmp_eax, !0x21
    0F84 %h                     ; je %Update_Pointer_1

    # Check for @
    83F8 40                     ; cmp_eax, !0x40
    0F84 %g                     ; je %Update_Pointer_2

    # Check for $
    83F8 24                     ; cmp_eax, !0x24
    0F84 %g                     ; je %Update_Pointer_2

    # Check for %
    83F8 25                     ; cmp_eax, !0x25
    0F84 %f                     ; je %Update_Pointer_4

    # Check for &
    83F8 26                     ; cmp_eax, !0x26
    0F84 %f                     ; je %Update_Pointer_4

    # deal with bad input
    E8 %R                       ; call %fail

:f #:Update_Pointer_4
    83C7 02                     ; add_edi, !2                 # Increment IP
:g #:Update_Pointer_2
    83C7 01                     ; add_edi, !1                 # Increment IP
:h #:Update_Pointer_1
    83C7 01                     ; add_edi, !1                 # Increment IP
    C3                          ; ret

:i #:First_pass_pointer
    # Deal with Pointer to label
    E8 %e                       ; call %Update_Pointer        # Increment IP
    BB 79850408                 ; mov_ebx, &table             # Using scratch
    E8 %B                       ; call %consume_token         # Read token
    E8 %I                       ; call %ClearScratch          # Throw away token
    83F8 3E                     ; cmp_eax, !0x3E              # check for '>'
    0F85 %b                     ; jne %First_pass             # Loop again

    # Deal with %label>label case
    BB 79850408                 ; mov_ebx, &table             # Write to scratch
    E8 %B                       ; call %consume_token         # get token
    E8 %I                       ; call %ClearScratch          # Clean up after ourselves
    E9 %b                       ; jmp %First_pass             # Loop again

:j # :First_pass_done
    C3                          # ret

:k #:hex
    # deal with EOF
    83F8 FC                     ; cmp_eax, !-4
    0F84 %o                     ; je %EOF
    # deal with line comments starting with #
    83F8 23                     ; cmp_eax, !0x23
    0F84 %t                     ; je %ascii_comment
    # deal with line comments starting with ;
    83F8 3B                     ; cmp_eax, !0x3B
    0F84 %t                     ; je %ascii_comment
    # deal all ascii less than 0
    83F8 30                     ; cmp_eax, !0x30
    0F8C %s                     ; jl %ascii_other
    # deal with 0-9
    83F8 3A                     ; cmp_eax, !0x3A
    0F8C %p                     ; jl %ascii_num
    # deal with all ascii less than A
    83F8 41                     ; cmp_eax, !0x41
    0F8C %s                     ; jl %ascii_other
    # deal with A-F
    83F8 47                     ; cmp_eax, !0x47
    0F8C %r                     ; jl %ascii_high
    # deal with all ascii less than a
    83F8 61                     ; cmp_eax, !0x61
    0F8C %s                     ; jl %ascii_other
    # deal with a-f
    83F8 67                     ; cmp_eax, !0x67
    0F8C %q                     ; jl %ascii_low
    # The rest that remains needs to be ignored
    E9 %s                       ; jmp %ascii_other

:l #:Second_pass
    E8 %y                       ; call %Read_byte

    # Deal with EOF
    83F8 FC                     ; cmp_eax, !-4
    0F84 %n                     ; je %Second_pass_done

    # Simply drop the label
    83F8 3A                     ; cmp_eax, !0x3A
    0F85 %m                     ; jne %Second_pass_0

    BB 79850408                 ; mov_ebx, &table             # Using scratch
    E8 %B                       ; call %consume_token         # Read token
    E8 %I                       ; call %ClearScratch          # Throw away token

    E9 %l                       ; jmp %Second_pass

:m #:Second_pass_0
    # Deal with % pointer
    83F8 25                     ; cmp_eax, !0x25
    0F84 %M                     ; je %StorePointer_rel4

    # Deal with @ pointer
    83F8 40                     ; cmp_eax, !0x40
    0F84 %N                     ; je %StorePointer_rel2

    # Deal with ! pointer
    83F8 21                     ; cmp_eax, !0x21
    0F84 %O                     ; je %StorePointer_rel1

    # Deal with & pointer
    83F8 26                     ; cmp_eax, !0x26
    0F84 %P                     ; je %StorePointer_abs4

    # Deal with $ pointer
    83F8 24                     ; cmp_eax, !0x24
    0F84 %Q                     ; je %StorePointer_abs2

#:Second_pass_1
    # Deal with everything else
    E8 %k                       ; call %hex                   # Process our char

    # Deal with EOF
    83F8 FC                     ; cmp_eax, !-4
    0F84 %n                     ; je %Second_pass_done

    # deal with -1 values
    83F8 00                     ; cmp_eax, !0
    0F8C %l                     ; jl %Second_pass

    # deal with toggle
    8B1D 69850408               ; mov_ebx,[DWORD] &Flag
    83FB 00                     ; cmp_ebx, !0
    0F84 %v                     ; je %print

    # process first byte of pair
    C1E0 04                     ; shl_eax, !4
    A3 65850408                 ; mov_[DWORD],eax &High
    B8 00000000                 ; mov_eax, %0
    A3 69850408                 ; mov_[DWORD],eax &Flag
    E9 %l                       ; jmp %Second_pass

:n #:Second_pass_done
:o #:EOF
    C3                          ; ret
:p #:ascii_num
    83E8 30                     ; sub_eax, !0x30
    C3                          ; ret
:q #:ascii_low
    83E8 57                     ; sub_eax, !0x57
    C3                          ; ret
:r #:ascii_high
    83E8 37                     ; sub_eax, !0x37
    C3                          ; ret
:s #:ascii_other
    B8 FFFFFFFF                 ; mov_eax, %-1
    C3                          ; ret
:t #:ascii_comment
    E8 %y                       ; call %Read_byte
    83F8 0D                     ; cmp_eax, !0xD
    0F84 %u                     ; je %ascii_comment_cr
    83F8 0A                     ; cmp_eax, !0xA
    0F85 %t                     ; jne %ascii_comment
:u #:ascii_comment_cr
    B8 FFFFFFFF                 ; mov_eax, %-1
    C3                          ; ret

# process second byte of pair
:v #:print
    # update the sum and store in output
    0305 65850408               ; add_eax,[DWORD] &High
    A2 79850408                 ; mov_[DWORD],al &table

    # Print our first Hex
    BA 01000000                 ; mov_edx, %1                 # set the size of chars we want
    E8 %A                       ; call %print_chars

    # flip the toggle
    A1 69850408                 ; mov_eax,[DWORD] &Flag
    F7D0                        ; not_eax
    A3 69850408                 ; mov_[DWORD],eax &Flag

    83C7 01                     ; add_edi, !1                 # Increment IP
    E9 %l                       ; jmp %Second_pass

:w #:Done
    # program completed Successfully
    BB 00000000                 ; mov_ebx, %0                 # All is well
    B8 01000000                 ; mov_eax, %1                 # put the exit syscall number in eax
    CD 80                       ; int !0x80                   # Call it a good day


# Malloc isn't actually required if the program being built fits in the initial memory
# However, it doesn't take much to add it.
# Requires a value in EBX
:x #:malloc
    B8 2D000000                 ; mov_eax, %45                # the Syscall # for SYS_BRK
    56                          ; push_esi                    # Protect esi
    57                          ; push_edi                    # Protect edi
    CD 80                       ; int !0x80                   # call the Kernel
    5F                          ; pop_edi                     # Restore edi
    5E                          ; pop_esi                     # Restore esi
    C3                          ; ret


:y #:Read_byte
    # Attempt to read 1 byte from STDIN
    56                          ; push_esi                    # Protect esi
    57                          ; push_edi                    # Protect edi
    53                          ; push_ebx                    # Protect ebx
    51                          ; push_ecx                    # Protect ecx

    BA 01000000                 ; mov_edx, %1                 # set the size of chars we want
    B9 75850408                 ; mov_ecx, &write             # Where to put it
    8B1D 6D850408               ; mov_ebx,[DWORD] &Input      # Where are we reading from
    B8 03000000                 ; mov_eax, %3                 # the syscall number for read
    CD 80                       ; int !0x80                   # call the Kernel

    59                          ; pop_ecx                     # Restore ecx
    5B                          ; pop_ebx                     # Restore ebx
    5F                          ; pop_edi                     # Restore edi
    5E                          ; pop_esi                     # Restore esi

    85C0                        ; test_eax,eax                # check what we got
    0F84 %z                     ; je %Read_byte_1             # Got EOF call it done

    # load byte
    A0 75850408                 ; mov_al,[DWORD] &write       # load char
    0FB6C0                      ; movzx_eax,al                # We have to zero extend it to use it
    C3                          ; ret

# Deal with EOF
:z #:Read_byte_1
    B8 FCFFFFFF                 ; mov_eax, %-4                # Put EOF in eax
    C3                          ; ret

:A #:print_chars
    56                          ; push_esi                    # Protect esi
    57                          ; push_edi                    # Protect edi
    53                          ; push_ebx                    # Protect ebx
    51                          ; push_ecx                    # Protect ecx

    B9 79850408                 ; mov_ecx, &table             # What we are writing
    8B1D 71850408               ; mov_ebx,[DWORD] &Output     # Write to target file
    B8 04000000                 ; mov_eax, %4                 # the syscall number for write
    # edx contains the number of bytes to write
    CD 80                       ; int !0x80                   # call the Kernel

    59                          ; pop_ecx                     # Restore ecx
    5B                          ; pop_ebx                     # Restore ebx
    5F                          ; pop_edi                     # Restore edi
    5E                          ; pop_esi                     # Restore esi
    C3                          ; ret

# Receives pointer in EBX
# Writes out char and updates EBX
:B #:consume_token
    E8 %y                       ; call %Read_byte             # Consume_token

    # Check for \t
    83F8 09                     ; cmp_eax, !0x09
    0F84 %C                     ; je %consume_token_done

    # Check for \n
    83F8 0A                     ; cmp_eax, !0x0A
    0F84 %C                     ; je %consume_token_done

    # Check for ' '
    83F8 20                     ; cmp_eax, !0x20
    0F84 %C                     ; je %consume_token_done

    # Check for '>'
    83F8 3E                     ; cmp_eax, !0x3E
    0F84 %C                     ; je %consume_token_done

    # Looks like we are still reading token
    8803                        ; mov_[ebx],al                # Store char
    83C3 01                     ; add_ebx, !1                 # Point to next spot
    E9 %B                       ; jmp %consume_token          # loop until done

:C #:consume_token_done
    B9 00000000                 ; mov_ecx, %0                 # Pad with nulls
    890B                        ; mov_[ebx],ecx
    83C3 04                     ; add_ebx, !4
    C3                          ; ret

:D #:StoreLabel
    89E8                        ; mov_eax,ebp                 # ENTRY
    83C5 18                     ; add_ebp, !24                # CALLOC
    8978 08                     ; mov_[eax+BYTE],edi !8       # ENTRY->TARGET = IP
    8930                        ; mov_[eax],esi               # ENTRY->NEXT = JUMP_TABLE
    89C6                        ; mov_esi,eax                 # JUMP_TABLE = ENTRY
    896E 10                     ; mov_[esi+BYTE],ebp !16      # ENTRY->NAME = TOKEN
    89EB                        ; mov_ebx,ebp                 # Write Starting after struct
    E8 %B                       ; call %consume_token         # Collect whole string
    89DD                        ; mov_ebp,ebx                 # Update HEAP
    E9 %b                       ; jmp %First_pass

:E #:GetTarget
    53                          ; push_ebx                    # protect ebx
    51                          ; push_ecx                    # protect ecx
    52                          ; push_edx                    # protect edx
    56                          ; push_esi                    # protect JUMP_TABLE
    B9 79850408                 ; mov_ecx, &table             # Reset scratch
    8B56 10                     ; mov_edx,[esi+BYTE] !16      # I->NAME
:F #:GetTarget_loop
    8A01                        ; mov_al,[ecx]                # I->NAME[0]
    8A1A                        ; mov_bl,[edx]                # scratch[0]
    0FB6DB                      ; movzx_ebx,bl                # Zero extend
    0FB6C0                      ; movzx_eax,al                # Zero extend
    38D8                        ; cmp_al,bl                   # IF TOKEN == I->NAME
    0F85 %G                     ; jne %GetTarget_miss         # Oops

    83C1 01                     ; add_ecx, !1
    83C2 01                     ; add_edx, !1
    3C 00                       ; cmp_al, !0
    0F85 %F                     ; jne %GetTarget_loop         # Loop until
    E9 %H                       ; jmp %GetTarget_done         # Match

    # Miss
:G #:GetTarget_miss
    8B36                        ; mov_esi,[esi]               # I = I->NEXT
    83FE 00                     ; cmp_esi, !0                 # IF NULL == I
    0F84 %R                     ; je %fail                    # Abort hard

    8B56 10                     ; mov_edx,[esi+BYTE] !16      # I->NAME
    B9 79850408                 ; mov_ecx, &table             # Reset scratch
    E9 %F                       ; jmp %GetTarget_loop

:H #:GetTarget_done
    8B46 08                     ; mov_eax,[esi+BYTE] !8       # Get address
    5E                          ; pop_esi                     # Restore JUMP_TABLE
    5A                          ; pop_edx                     # Restore edx
    59                          ; pop_ecx                     # Restore ecx
    5B                          ; pop_ebx                     # Restore ebx
    C3                          ; ret

:I #:ClearScratch
    50                          ; push_eax                    # Protect against changes
    53                          ; push_ebx                    # And overwrites
    51                          ; push_ecx                    # While we work
    BB 79850408                 ; mov_ebx, &table             # Where our table is
    B8 00000000                 ; mov_eax, %0                 # Using null

:J #:ClearScratch_loop
    8B0B                        ; mov_ecx,[ebx]               # Get current value
    8803                        ; mov_[ebx],al                # Because we want null
    83C3 01                     ; add_ebx, !1                 # Increment
    83F9 00                     ; cmp_ecx, !0                 # Check if we hit null
    0F85 %J                     ; jne %ClearScratch_loop      # Keep looping

    59                          ; pop_ecx                     # Restore
    5B                          ; pop_ebx                     # Damage
    58                          ; pop_eax                     # Entirely
    C3                          ; ret

:K #:StorePointer
    E8 %e                       ; call %Update_Pointer        # Increment IP
    BB 79850408                 ; mov_ebx, &table             # Write to scratch
    E8 %B                       ; call %consume_token         # get token
    50                          ; push_eax                    # Protect base_sep_p
    B8 79850408                 ; mov_eax, &table             # Pointer to scratch
    E8 %E                       ; call %GetTarget             # Get address of pointer
    E8 %I                       ; call %ClearScratch          # Clean up after ourselves
    89FA                        ; mov_edx,edi                 # base = IP
    5B                          ; pop_ebx                     # Restore base_sep_p
    83FB 3E                     ; cmp_ebx, !0x3E              # If base_sep_p == '>'
    0F85 %L                     ; jne %StorePointer_done      # If not

    # Deal with %label>label case
    50                          ; push_eax                    # We need to preserve main target
    BB 79850408                 ; mov_ebx, &table             # Write to scratch
    E8 %B                       ; call %consume_token         # get token
    B8 79850408                 ; mov_eax, &table             # Pointer to scratch
    E8 %E                       ; call %GetTarget             # Get address of pointer
    E8 %I                       ; call %ClearScratch          # Clean up after ourselves
    89C2                        ; mov_edx,eax                 # Use our new base
    58                          ; pop_eax                     # Restore main target

:L #:StorePointer_done
    C3                          ; ret

:M #:StorePointer_rel4
    E8 %K                       ; call %StorePointer          # Do Common
    29D0                        ; sub_eax,edx                 # target - ip
    A3 79850408                 ; mov_[DWORD],eax &table      # put value in output
    BA 04000000                 ; mov_edx, %4                 # set the size of chars we want
    E8 %A                       ; call %print_chars
    E8 %I                       ; call %ClearScratch          # Clean up after ourselves
    E9 %l                       ; jmp %Second_pass

:N #:StorePointer_rel2
    E8 %K                       ; call %StorePointer          # Do Common
    29D0                        ; sub_eax,edx                 # target - ip
    A3 79850408                 ; mov_[DWORD],eax &table      # put value in output
    BA 02000000                 ; mov_edx, %2                 # set the size of chars we want
    E8 %A                       ; call %print_chars
    E8 %I                       ; call %ClearScratch          # Clean up after ourselves
    E9 %l                       ; jmp %Second_pass

:O #:StorePointer_rel1
    E8 %K                       ; call %StorePointer          # Do Common
    29D0                        ; sub_eax,edx                 # target - ip
    A3 79850408                 ; mov_[DWORD],eax &table      # put value in output
    BA 01000000                 ; mov_edx, %1                 # set the size of chars we want
    E8 %A                       ; call %print_chars
    E8 %I                       ; call %ClearScratch          # Clean up after ourselves
    E9 %l                       ; jmp %Second_pass

:P #:StorePointer_abs4
    E8 %K                       ; call %StorePointer          # Do Common
    A3 79850408                 ; mov_[DWORD],eax &table      # put value in output
    BA 04000000                 ; mov_edx, %4                 # set the size of chars we want
    E8 %A                       ; call %print_chars
    E8 %I                       ; call %ClearScratch          # Clean up after ourselves
    E9 %l                       ; jmp %Second_pass

:Q #:StorePointer_abs2
    E8 %K                       ; call %StorePointer          # Do Common
    A3 79850408                 ; mov_[DWORD],eax &table      # put value in output
    BA 02000000                 ; mov_edx, %2                 # set the size of chars we want
    E8 %A                       ; call %print_chars
    E8 %I                       ; call %ClearScratch          # Clean up after ourselves
    E9 %l                       ; jmp %Second_pass

:R #:fail
    # Something went wrong
    BB 01000000                 ; mov_ebx, %1                 # All is wrong
    B8 01000000                 ; mov_eax, %1                 # put the exit syscall number in eax
    CD 80                       ; int !0x80                   # Call it a good day


#:High (0x8048565)
    00000000                    ; %0
#:Flag (0x8048569)
    00000000                    ; %0
#:Input (0x804856D)
    00000000                    ; %0
#:Output (0x8048571)
    00000000                    ; %0
#:write (0x8048575)
    00000000                    ; %0
#:table (0x8048579)
    00000000                    ; %0
#:ELF_end

File /x86/catm_x86.hex2

Live-bootstrap source file is 'seed/stage0-posix/x86/catm_x86.hex2'.
URL: https://github.com/oriansj/stage0-posix-x86/blob/83508012c952e3aa3f70a89fd7d85d9a1af6f305/catm_x86.hex2
# SPDX-FileCopyrightText: © 2019 Jeremiah Orians
#
# SPDX-License-Identifier: GPL-3.0-or-later

    # Register usage:
    # EBP => OUTPUT
    # EDI => Buffer
    # ESI => INPUT

## ELF Header
:ELF_base
7F 45 4C 46                    # e_ident[EI_MAG0-3] ELF's magic number

01                             # e_ident[EI_CLASS] Indicating 32 bit
01                             # e_ident[EI_DATA] Indicating little endianness
01                             # e_ident[EI_VERSION] Indicating original elf

03                             # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                             # e_ident[EI_ABIVERSION] Set at 0 because none cares

00 00 00 00 00 00 00           # e_ident[EI_PAD]

02 00                          # e_type Indicating Executable
03 00                          # e_machine Indicating x86
01 00 00 00                    # e_version Indicating original elf

&_start                        # e_entry Address of the entry point
%ELF_program_headers>ELF_base  # e_phoff Address of program header table
00 00 00 00                    # e_shoff Address of section header table

00 00 00 00                    # e_flags

34 00                          # e_ehsize Indicating our 52 Byte header

20 00                          # e_phentsize size of a program header table
01 00                          # e_phnum number of entries in program table

00 00                          # e_shentsize size of a section header table
00 00                          # e_shnum number of entries in section table

00 00                          # e_shstrndx index of the section names

## Program Header
:ELF_program_headers
:ELF_program_header__text
01 00 00 00                    # ph_type: PT-LOAD = 1
00 00 00 00                    # ph_offset

&ELF_base                      # ph_vaddr
&ELF_base                      # ph_physaddr

%ELF_end>ELF_base              # ph_filesz
%ELF_end>ELF_base              # ph_memsz

07 00 00 00                    # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                    # ph_align

:ELF_text

:_start
    58                          ; pop_eax                     # Get the number of arguments
    5B                          ; pop_ebx                     # Get the program name
    5B                          ; pop_ebx                     # Get the actual output name
    66B9 4102                   ; mov_cx, @577                # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    66BA 8001                   ; mov_dx, @384                # Prepare file as RW for owner only (600 in octal)
    6A 05                       ; push !5                     # prepare to set eax to 5
    58                          ; pop_eax                     # the syscall number for open()
    CD 80                       ; int !0x80                   # Now open that file
    89C5                        ; mov_ebp,eax                 # Preserve the file pointer we were given

    6A 2D                       ; push !45
    58                          ; pop_eax                     # the Syscall # for SYS_BRK
    31DB                        ; xor_ebx,ebx                 # Get current brk
    CD 80                       ; int !0x80                   # Let the kernel do the work
    89C7                        ; mov_edi,eax                 # Set our malloc pointer

    6A 2D                       ; push !45
    58                          ; pop_eax                     # the Syscall # for SYS_BRK
    89FB                        ; mov_ebx,edi                 # Using current pointer
    81C3 00001000               ; add_ebx, %0x100000          # Allocate 1MB
    CD 80                       ; int !0x80                   # Let the kernel do the work

:core
    5B                          ; pop_ebx                     # Get the actual input name
    85DB                        ; test_ebx,ebx                # Check for null string
    74 !done                    ; je !done                    # Hit null be done

    31C9                        ; xor_ecx,ecx                 # prepare read_only
    31D2                        ; xor_edx,edx                 # prevent any interactions
    6A 05                       ; push !5
    58                          ; pop_eax                     # the syscall number for open()
    CD 80                       ; int !0x80                   # Now open that damn file
    89C6                        ; mov_esi,eax                 # Protect INPUT
:keep
    BA 00001000                 ; mov_edx, %0x100000          # set the size of chars we want
    89F9                        ; mov_ecx,edi                 # Where to put it
    89F3                        ; mov_ebx,esi                 # Where are we reading from
    6A03                        ; push !3
    58                          ; pop_eax                     # the syscall number for read
    CD 80                       ; int !0x80                   # call the Kernel
    50                          ; push_eax                    # Protect the number of bytes read

    89C2                        ; mov_edx,eax                 # Number of bytes to write
    89F9                        ; mov_ecx,edi                 # What we are writing
    89EB                        ; mov_ebx,ebp                 # Write to target file
    6A 04                       ; push !4
    58                          ; pop_eax                     # the syscall number for write
    CD 80                       ; int !0x80                   # call the Kernel

    58                          ; pop_eax                     # Get bytes read
    3D 00001000                 ; cmp_eax, %0x100000          # Check if buffer was fully used
    74 !keep                    ; je !keep                    # Keep looping if was full
    EB !core                    ; jmp !core                   # Otherwise move to next file

:done
    # program completed Successfully
    31DB                        ; xor_ebx,ebx                 # All is well
    6A 01                       ; push !1
    58                          ; pop_eax                     # put the exit syscall number in eax
    CD 80                       ; int !0x80                   # Call it a good day

:ELF_end

File /x86/ELF-i386.hex2

Live-bootstrap source file is 'seed/stage0-posix/x86/ELF-i386.hex2'.
URL: https://github.com/oriansj/stage0-posix-x86/blob/83508012c952e3aa3f70a89fd7d85d9a1af6f305/ELF-i386.hex2
### Copyright (C) 2016 Jeremiah Orians
### Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
### This file is part of stage0.
###
### stage0 is free software: you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation, either version 3 of the License, or
### (at your option) any later version.
###
### stage0 is distributed in the hope that it will be useful,
### but WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
### GNU General Public License for more details.
###
### You should have received a copy of the GNU General Public License
### along with stage0.  If not, see <http://www.gnu.org/licenses/>.

### stage0's hex2 format
###    !<label>          1 byte relative
###    $<label>          2 byte address
###    @<label>          2 byte relative
###    &<label>          4 byte address
###    %<label>          4 byte relative

### elf32.hex2: 32 bit elf header in hex2
### if you wish to use this header, you need to add :ELF_end to the end of your
### M1 or hex2 files.

## ELF Header
:ELF_base
7F 45 4C 46                    # e_ident[EI_MAG0-3] ELF's magic number

01                             # e_ident[EI_CLASS] Indicating 32 bit
01                             # e_ident[EI_DATA] Indicating little endianness
01                             # e_ident[EI_VERSION] Indicating original elf

03                             # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                             # e_ident[EI_ABIVERSION] Set at 0 because none cares

00 00 00 00 00 00 00           # e_ident[EI_PAD]

02 00                          # e_type Indicating Executable
03 00                          # e_machine Indicating x86
01 00 00 00                    # e_version Indicating original elf

&_start                        # e_entry Address of the entry point
%ELF_program_headers>ELF_base  # e_phoff Address of program header table
00 00 00 00                    # e_shoff Address of section header table

00 00 00 00                    # e_flags

34 00                          # e_ehsize Indicating our 52 Byte header

20 00                          # e_phentsize size of a program header table
01 00                          # e_phnum number of entries in program table

00 00                          # e_shentsize size of a section header table
00 00                          # e_shnum number of entries in section table

00 00                          # e_shstrndx index of the section names

## Program Header
:ELF_program_headers
:ELF_program_header__text
01 00 00 00                    # ph_type: PT-LOAD = 1
00 00 00 00                    # ph_offset

&ELF_base                      # ph_vaddr
&ELF_base                      # ph_physaddr

%ELF_end>ELF_base              # ph_filesz
%ELF_end>ELF_base              # ph_memsz

07 00 00 00                    # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                    # ph_align

:ELF_text

File /x86/M0_x86.hex2

Live-bootstrap source file is 'seed/stage0-posix/x86/M0_x86.hex2'.
URL: https://github.com/oriansj/stage0-posix-x86/blob/83508012c952e3aa3f70a89fd7d85d9a1af6f305/M0_x86.hex2
# SPDX-FileCopyrightText: © 2017 Jeremiah Orians
#
# SPDX-License-Identifier: GPL-3.0-or-later

    # Register usage:
    # EAX, ECX, EBX => Temps
    # EDI => MALLOC
    # EBP => HEAD
    # [Output] => Output_file
    # [Input] => Input_file

    # Struct format: (size 32)
    # NEXT => 0
    # TYPE => 8
    # TEXT => 16
    # EXPRESSION => 24

    # Types
    # None => 0
    # MACRO => 1
    # STRING => 2

# Where the ELF Header is going to hit
# Simply jump to _start
# Our main function

:_start
    58                          ; pop_eax                     # Get the number of arguments
    5B                          ; pop_ebx                     # Get the program name
    5B                          ; pop_ebx                     # Get the actual input name
    B9 00000000                 ; mov_ecx, %0                 # prepare read_only
    B8 05000000                 ; mov_eax, %5                 # the syscall number for open()
    CD 80                       ; int !0x80                   # Now open that damn file
    A3 &Input                   ; mov_[DWORD],eax &Input      # Preserve the file pointer we were given

    5B                          ; pop_ebx                     # Get the actual output name
    B9 41020000                 ; mov_ecx, %577               # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    BA 80010000                 ; mov_edx, %384               # Prepare file as RW for owner only (600 in octal)
    B8 05000000                 ; mov_eax, %5                 # the syscall number for open()
    CD 80                       ; int !0x80                   # Now open that damn file
    83F8 00                     ; cmp_eax, !0                 # Check for missing output
    0F8F %_start_out            ; jg %_start_out              # Have real input
    B8 01000000                 ; mov_eax, %1                 # Use stdout

:_start_out
    A3 &Output                  ; mov_[DWORD],eax &Output     # Preserve the file pointer we were given

    B8 2D000000                 ; mov_eax, %45                # the Syscall # for SYS_BRK
    BB 00000000                 ; mov_ebx, %0                 # Get current brk
    CD 80                       ; int !0x80                   # Let the kernel do the work
    89C7                        ; mov_edi,eax                 # Set our malloc pointer

    E8 %Tokenize_Line           ; call %Tokenize_Line         # Get all lines
    89E8                        ; mov_eax,ebp                 # prepare for Reverse_List
    E8 %Reverse_List            ; call %Reverse_List          # Correct order
    89C5                        ; mov_ebp,eax                 # Update HEAD
    E8 %Identify_Macros         ; call %Identify_Macros       # Find the DEFINEs
    E8 %Line_Macro              ; call %Line_Macro            # Apply the DEFINEs
    E8 %Process_String          ; call %Process_String        # Handle strings
    E8 %Eval_Immediates         ; call %Eval_Immediates       # Handle Numbers
    E8 %Preserve_Other          ; call %Preserve_Other        # Collect the remaining
    E8 %Print_Hex               ; call %Print_Hex             # Output our results

:Done
    # program completed Successfully
    BB 00000000                 ; mov_ebx, %0                 # All is well
    B8 01000000                 ; mov_eax, %1                 # put the exit syscall number in eax
    CD 80                       ; int !0x80                   # Call it a good day


# Tokenize_Line Function
# Using input file [Input] and Head EBP
# Creates a linked list of structs
# Uses EBX for in_set strings, ECX for Int C and EDX for Struct Token* p
:Tokenize_Line
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    52                          ; push_edx                    # Protect EDX
:restart
    E8 %fgetc                   ; call %fgetc                 # Read a char
    83F8 FC                     ; cmp_eax, !-4                # Check for EOF
    0F84 %done                  ; je %done                    # File is collected

    0FB6C0                      ; movzx_eax,al                # We have to zero extend it to use it
    89C1                        ; mov_ecx,eax                 # Protect C

    BB &comments                ; mov_ebx, &comments          # Get pointer to "#;"
    E8 %In_Set                  ; call %In_Set                # Check for comments
    83F8 01                     ; cmp_eax, !1                 # If comments
    0F84 %Purge_LineComment     ; je %Purge_LineComment       # try again

    89C8                        ; mov_eax,ecx                 # put C in place for check
    BB &terminators             ; mov_ebx, &terminators       # Get pointer to "\n\t "
    E8 %In_Set                  ; call %In_Set                # Check for terminators
    83F8 01                     ; cmp_eax, !1                 # If terminator
    0F84 %restart               ; je %restart                 # try again

    B8 20000000                 ; mov_eax, %32                # Malloc the struct P
    E8 %malloc                  ; call %malloc                # Get pointer to P
    89C2                        ; mov_edx,eax                 # Protect P
    892A                        ; mov_[edx],ebp               # P->NEXT = HEAD
    89D5                        ; mov_ebp,edx                 # HEAD = P

    89C8                        ; mov_eax,ecx                 # put C in place for check
    BB &string_char             ; mov_ebx, &string_char       # Get pointer to "\"'"
    E8 %In_Set                  ; call %In_Set                # Check for string chars
    83F8 01                     ; cmp_eax, !1                 # If string char
    0F84 %Store_String          ; je %Store_String            # Get string

    E8 %Store_Atom              ; call %Store_Atom            # Get whole token
    E9 %restart                 ; jmp %restart

:done
    5A                          ; pop_edx                     # Restore EDX
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    C3                          ; ret


# fgetc function
# Receives FILE* in [Input]
# Returns -4 (EOF) or char in EAX
:fgetc
    52                          ; push_edx                    # Protect EDX
    51                          ; push_ecx                    # Protect ECX
    53                          ; push_ebx                    # Protect EBX
    B8 FCFFFFFF                 ; mov_eax, %-4                # Put EOF in eax
    50                          ; push_eax                    # Assume bad (If nothing read, value will remain EOF)
    8D0C24                      ; lea_ecx,[esp]               # Get stack address
    8B1D &Input                 ; mov_ebx,[DWORD] &Input      # Where are we reading from
    B8 03000000                 ; mov_eax, %3                 # the syscall number for read
    BA 01000000                 ; mov_edx, %1                 # set the size of chars we want
    CD 80                       ; int !0x80                   # call the Kernel
    58                          ; pop_eax                     # Get either char or EOF
    5B                          ; pop_ebx                     # Restore EBX
    59                          ; pop_ecx                     # Restore ECX
    5A                          ; pop_edx                     # Restore EDX
    C3                          ; ret


# Malloc isn't actually required if the program being built fits in the initial memory
# However, it doesn't take much to add it.
# Requires EDI to be initialized and EAX to have the number of desired bytes
:malloc
    52                          ; push_edx                    # Protect EDX
    51                          ; push_ecx                    # Protect ECX
    53                          ; push_ebx                    # Protect EBX

    89FB                        ; mov_ebx,edi                 # Using the current pointer
    01C3                        ; add_ebx,eax                 # Request the number of desired bytes
    B8 2D000000                 ; mov_eax, %45                # the Syscall # for SYS_BRK
    CD 80                       ; int !0x80                   # call the Kernel
    89F8                        ; mov_eax,edi                 # Return pointer
    89DF                        ; mov_edi,ebx                 # Update pointer

    5B                          ; pop_ebx                     # Restore EBX
    59                          ; pop_ecx                     # Restore ECX
    5A                          ; pop_edx                     # Restore EDX
    C3                          ; ret


# Purge_LineComment function
# Reads chars until LF and jumps to restart
:Purge_LineComment
    E8 %fgetc                   ; call %fgetc                 # Get a char
    0FB6C0                      ; movzx_eax,al                # Zero extend
    83F8 0A                     ; cmp_eax, !10                # While not LF
    0F85 %Purge_LineComment     ; jne %Purge_LineComment      # Keep reading
    E9 %restart                 ; jmp %restart


# Store_String Function
# Receives C in ECX, HEAD in EDX and Input file in [Output]
# Uses EBX for terminator, ECX for C and EDX for string
:Store_String
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    52                          ; push_edx                    # Protect EDX

    B8 02000000                 ; mov_eax, %2                 # Using TYPE STRING
    8942 08                     ; mov_[edx+BYTE],eax !8       # HEAD->TYPE = STRING
    B8 00010000                 ; mov_eax, %256               # Malloc the string
    E8 %malloc                  ; call %malloc                # Get pointer to P
    8942 10                     ; mov_[edx+BYTE],eax !16      # HEAD->TEXT = STRING
    89CB                        ; mov_ebx,ecx                 # Protect terminator
    89C2                        ; mov_edx,eax                 # Protect string pointer
:Store_String_Loop
    880A                        ; mov_[edx],cl                # write byte
    E8 %fgetc                   ; call %fgetc                 # read next char
    0FB6C0                      ; movzx_eax,al                # Zero extend it
    89C1                        ; mov_ecx,eax                 # Update C
    83C2 01                     ; add_edx, !1                 # STRING = STRING + 1
    39D9                        ; cmp_ebx,ecx                 # See if we hit terminator
    0F85 %Store_String_Loop     ; jne %Store_String_Loop      # Otherwise keep looping

    5A                          ; pop_edx                     # Restore EDX
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    89D0                        ; mov_eax,edx                 # return HEAD
    E9 %restart                 ; jmp %restart


# Store_Atom Function
# Receives C in ECX, HEAD in EDX and Input file in [Input]
# Uses EBX for in_set strings, ECX for C and EDX for string
:Store_Atom
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    52                          ; push_edx                    # Protect EDX

    B8 00010000                 ; mov_eax, %256               # Malloc the string
    E8 %malloc                  ; call %malloc                # Get pointer to P
    8942 10                     ; mov_[edx+BYTE],eax !16      # HEAD->TEXT = STRING
    BB &terminators             ; mov_ebx, &terminators       # Get pointer to "\n\t "
    89C2                        ; mov_edx,eax                 # Protect string pointer
:Store_Atom_loop
    880A                        ; mov_[edx],cl                # write byte
    E8 %fgetc                   ; call %fgetc                 # read next char
    0FB6C0                      ; movzx_eax,al                # Zero extend it
    89C1                        ; mov_ecx,eax                 # Update C
    83C2 01                     ; add_edx, !1                 # STRING = STRING + 1
    E8 %In_Set                  ; call %In_Set                # Check for terminators
    83F8 00                     ; cmp_eax, !0                 # Check for "\n\t "
    0F84 %Store_Atom_loop       ; je %Store_Atom_loop         # Loop otherwise

    5A                          ; pop_edx                     # Restore EDX
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    89D0                        ; mov_eax,edx                 # return HEAD
    C3                          ; ret


# In_Set function
# Receives Char C in EAX and CHAR* in EBX
# Returns 1 if true, zero if false in EAX
:In_Set
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
:In_Set_loop
    8A0B                        ; mov_cl,[ebx]                # Read char
    0FB6C9                      ; movzx_ecx,cl                # Zero extend it

    39C8                        ; cmp_eax,ecx                 # See if they match
    0F84 %In_Set_True           ; je %In_Set_True             # return true

    83F9 00                     ; cmp_ecx, !0                 # Check for NULL
    0F84 %In_Set_False          ; je %In_Set_False            # return false

    83C3 01                     ; add_ebx, !1                 # s = s + 1
    E9 %In_Set_loop             ; jmp %In_Set_loop            # Keep looping

:In_Set_True
    B8 01000000                 ; mov_eax, %1                 # Set True
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    C3                          ; ret

:In_Set_False
    B8 00000000                 ; mov_eax, %0                 # Set FALSE
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    C3                          ; ret

# Char sets
:terminators
0A 09 20 00                         ; "\n\t "

:comments
3B 23 00                            ; ";#"

:string_char
22 27 00                            ; "\"\'"


# Reverse_List function
# Receives List in EAX
# Returns the list reversed in EAX
:Reverse_List
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    89C3                        ; mov_ebx,eax                 # Set HEAD
    B8 00000000                 ; mov_eax, %0                 # ROOT = NULL
:Reverse_List_Loop
    83FB 00                     ; cmp_ebx, !0                 # WHILE HEAD != NULL
    0F84 %Reverse_List_Done     ; je %Reverse_List_Done       # Stop otherwise

    8B0B                        ; mov_ecx,[ebx]               # NEXT = HEAD->NEXT
    8903                        ; mov_[ebx],eax               # HEAD->NEXT = ROOT
    89D8                        ; mov_eax,ebx                 # ROOT = HEAD
    89CB                        ; mov_ebx,ecx                 # HEAD = NEXT
    E9 %Reverse_List_Loop       ; jmp %Reverse_List_Loop      # Keep Going

:Reverse_List_Done
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    C3                          ; ret


# Identify_Macros function
# Receives List in EAX
# Updates the list in place; does not modify registers
# Uses EBX for DEFINE, ECX for I
:Identify_Macros
    50                          ; push_eax                    # Protect EAX
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    52                          ; push_edx                    # Protect EDX
    BB &DEFINE_str              ; mov_ebx, &DEFINE_str        # Setup define string
    89C1                        ; mov_ecx,eax                 # I = HEAD
:Identify_Macros_Loop
    8B41 10                     ; mov_eax,[ecx+BYTE] !16      # I->TEXT
    E8 %match                   ; call %match                 # IF "DEFINE" == I->TEXT
    83F8 00                     ; cmp_eax, !0                 # Check if match
    0F85 %Identify_Macros_Next  ; jne %Identify_Macros_Next   # Skip the work

    # Deal with MACRO
    B8 01000000                 ; mov_eax, %1                 # Using MACRO
    8941 08                     ; mov_[ecx+BYTE],eax !8       # I->TYPE = MACRO

    8B01                        ; mov_eax,[ecx]               # I->NEXT
    8B40 10                     ; mov_eax,[eax+BYTE] !16      # I->NEXT->TEXT
    8941 10                     ; mov_[ecx+BYTE],eax !16      # I->TEXT = I->NEXT->TEXT

    8B01                        ; mov_eax,[ecx]               # I->NEXT
    8B00                        ; mov_eax,[eax]               # I->NEXT->NEXT
    8B40 10                     ; mov_eax,[eax+BYTE] !16      # I->NEXT->NEXT->TEXT
    8941 18                     ; mov_[ecx+BYTE],eax !24      # I->EXPRESSION = I->NEXT->NEXT->TEXT

    8B01                        ; mov_eax,[ecx]               # I->NEXT
    8B00                        ; mov_eax,[eax]               # I->NEXT->NEXT
    8B00                        ; mov_eax,[eax]               # I->NEXT->NEXT->NEXT
    8901                        ; mov_[ecx],eax               # I->NEXT = I->NEXT->NEXT->NEXT

:Identify_Macros_Next
    8B09                        ; mov_ecx,[ecx]               # I = I->NEXT
    83F9 00                     ; cmp_ecx, !0                 # Check for NULL
    0F85 %Identify_Macros_Loop  ; jne %Identify_Macros_Loop   # Keep looping otherwise

    5A                          ; pop_edx                     # Restore EDX
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    58                          ; pop_eax                     # Restore EAX
    C3                          ; ret

:DEFINE_str
44 45 46 49 4E 45 00                ; "DEFINE"


# match function
# Receives CHAR* in EAX and CHAR* in EBX
# Returns 0 (TRUE) or 1 (FALSE) in EAX
:match
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    52                          ; push_edx                    # Protect EDX
    89C1                        ; mov_ecx,eax                 # S1 in place
    89DA                        ; mov_edx,ebx                 # S2 in place
:match_Loop
    8A01                        ; mov_al,[ecx]                # S1[0]
    0FB6C0                      ; movzx_eax,al                # Make it useful
    8A1A                        ; mov_bl,[edx]                # S2[0]
    0FB6DB                      ; movzx_ebx,bl                # Make it useful
    39D8                        ; cmp_eax,ebx                 # See if they match
    0F85 %match_False           ; jne %match_False            # If not

    83C1 01                     ; add_ecx, !1                 # S1 = S1 + 1
    83C2 01                     ; add_edx, !1                 # S2 = S2 + 1
    83F8 00                     ; cmp_eax, !0                 # If reached end of string
    0F84 %match_Done            ; je %match_Done              # Perfect match
    E9 %match_Loop              ; jmp %match_Loop             # Otherwise keep looping

:match_False
    B8 01000000                 ; mov_eax, %1                 # Return false
:match_Done
    5A                          ; pop_edx                     # Restore EDX
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    C3                          ; ret


# Line_Macro function
# Receives List in EAX
# Updates the list in place; does not modify registers
# Uses EAX for I, EBX for I->TEXT, ECX for I->EXPRESSION
:Line_Macro
    50                          ; push_eax                    # Protect EAX
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    52                          ; push_edx                    # Protect EDX
:Line_Macro_Loop
    8B58 08                     ; mov_ebx,[eax+BYTE] !8       # I->TYPE
    83FB 01                     ; cmp_ebx, !1                 # IF MACRO == I->TYPE
    0F85 %Line_Macro_Next       ; jne %Line_Macro_Next        # Otherwise move on

    # Is a macro apply
    8B58 10                     ; mov_ebx,[eax+BYTE] !16      # I->TEXT
    8B48 18                     ; mov_ecx,[eax+BYTE] !24      # I->EXPRESSION
    8B00                        ; mov_eax,[eax]               # I->NEXT
    E8 %Set_Expression          ; call %Set_Expression        # Apply it
    E9 %Line_Macro_Loop         ; jmp %Line_Macro_Loop        # Move on to next

:Line_Macro_Next
    8B00                        ; mov_eax,[eax]               # I->NEXT
    83F8 00                     ; cmp_eax, !0                 # Check for NULL
    0F85 %Line_Macro_Loop       ; jne %Line_Macro_Loop        # Keep going

    5A                          ; pop_edx                     # Restore EDX
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    58                          ; pop_eax                     # Restore EAX
    C3                          ; ret


# Set_Expression function
# Receives List in EAX, CHAR* in EBX and CHAR* in ECX
# Updates the list in place; does not modify registers
# Uses EBX for C, ECX for EXP and EDX for I
:Set_Expression
    50                          ; push_eax                    # Protect EAX
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    52                          ; push_edx                    # Protect EDX
    89C2                        ; mov_edx,eax                 # Set I
:Set_Expression_Loop
    8B42 08                     ; mov_eax,[edx+BYTE] !8       # I->TYPE
    83F8 01                     ; cmp_eax, !1                 # IF MACRO == I->TYPE
    0F84 %Set_Expression_Next   ; je %Set_Expression_Next     # Ignore and move on

    8B42 10                     ; mov_eax,[edx+BYTE] !16      # I->TEXT
    E8 %match                   ; call %match                 # Check for match
    83F8 00                     ; cmp_eax, !0                 # If match
    0F85 %Set_Expression_Next   ; jne %Set_Expression_Next    # Otherwise next

    # We have a non-macro match
    894A 18                     ; mov_[edx+BYTE],ecx !24      # I->EXPRESSION = EXP

:Set_Expression_Next
    8B12                        ; mov_edx,[edx]               # I = I->NEXT
    83FA 00                     ; cmp_edx, !0                 # IF NULL == I
    0F85 %Set_Expression_Loop   ; jne %Set_Expression_Loop    # Otherwise keep looping

    5A                          ; pop_edx                     # Restore EDX
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    58                          ; pop_eax                     # Restore EAX
    C3                          ; ret


# Process_String function
# Receives List in EAX
# Update the list in place; does not modify registers
# Uses EBX for I->TEXT, ECX for I and EDX for S
:Process_String
    50                          ; push_eax                    # Protect EAX
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    52                          ; push_edx                    # Protect EDX
    89C1                        ; mov_ecx,eax                 # I = HEAD
:Process_String_loop
    8B41 08                     ; mov_eax,[ecx+BYTE] !8       # I->TYPE
    83F8 02                     ; cmp_eax, !2                 # IF STRING == I->TYPE
    0F85 %Process_String_Next   ; jne %Process_String_Next    # Skip to next

    8B59 10                     ; mov_ebx,[ecx+BYTE] !16      # I->TEXT
    8A03                        ; mov_al,[ebx]                # I->TEXT[0]
    0FB6C0                      ; movzx_eax,al                # make it useful
    83F8 27                     ; cmp_eax, !39                # IF '\'' == I->TEXT[0]
    0F85 %Process_String_Raw    ; jne %Process_String_Raw     # Deal with '\"'

    # Deal with '\''
    83C3 01                     ; add_ebx, !1                 # I->TEXT + 1
    8959 18                     ; mov_[ecx+BYTE],ebx !24      # I->EXPRESSION = I->TEXT + 1
    E9 %Process_String_Next     ; jmp %Process_String_Next    # Move on to next

:Process_String_Raw
    89D8                        ; mov_eax,ebx                 # Get length of I->TEXT
    E8 %string_length           ; call %string_length         # Do it
    C1E8 02                     ; shr_eax, !2                 # LENGTH = LENGTH >> 2
    83C0 01                     ; add_eax, !1                 # LENGTH = LENGTH + 1
    C1E0 03                     ; shl_eax, !3                 # LENGTH = LENGTH << 3
    E8 %malloc                  ; call %malloc                # Get string
    89DA                        ; mov_edx,ebx                 # S = I->TEXT
    83C2 01                     ; add_edx, !1                 # S = S + 1
    8941 18                     ; mov_[ecx+BYTE],eax !24      # I->EXPRESSION = hexify
    89C3                        ; mov_ebx,eax                 # Put hexify buffer in ebx

:Process_String_Raw_Loop
    8A02                        ; mov_al,[edx]                # Read 1 chars
    0FB6C0                      ; movzx_eax,al                # Make it useful
    83C2 01                     ; add_edx, !1                 # S = S + 1
    3C 00                       ; cmp_al, !0                  # Check for NULL
    9C                          ; pushf                       # Protect condition
    E8 %hex8                    ; call %hex8                  # write them all
    9D                          ; popf                        # restore condition
    0F85 %Process_String_Raw_Loop ; jne %Process_String_Raw_Loop # Keep looping

:Process_String_Next
    8B09                        ; mov_ecx,[ecx]               # I = I->NEXT
    83F9 00                     ; cmp_ecx, !0                 # IF NULL == I
    0F85 %Process_String_loop   ; jne %Process_String_loop    # Otherwise keep looping

    5A                          ; pop_edx                     # Restore EDX
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    58                          ; pop_eax                     # Restore EAX
    C3                          ; ret


# string_length function
# Receives CHAR* in EAX
# Returns INT in EAX
# Uses EAX for CH, EBX for S and ECX for INDEX
:string_length
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    89C3                        ; mov_ebx,eax                 # Set S
    B9 00000000                 ; mov_ecx, %0                 # INDEX = 0
:string_length_loop
    8A040B                      ; mov_al,[ebx+ecx]            # S[0]
    0FB6C0                      ; movzx_eax,al                # make it useful
    83F8 00                     ; cmp_eax, !0                 # IF NULL == S[0]
    0F84 %string_length_done    ; je %string_length_done      # Stop

    83C1 01                     ; add_ecx, !1                 # INDEX = INDEX + 1
    E9 %string_length_loop      ; jmp %string_length_loop     # Keep going

:string_length_done
    89C8                        ; mov_eax,ecx                 # RETURN INDEX
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    C3                          ; ret


# Eval_Immediates function
# Receives List in EAX
# Updates the list in place; does not modify registers
# Uses EBX for I->TEXT[0], ECX for I->TEXT[1] and EDX for I
:Eval_Immediates
    50                          ; push_eax                    # Protect EAX
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    52                          ; push_edx                    # Protect EDX
    89C2                        ; mov_edx,eax                 # I = HEAD
:Eval_Immediates_Loop
    # Check for MACRO
    8B42 08                     ; mov_eax,[edx+BYTE] !8       # I->TYPE
    83F8 01                     ; cmp_eax, !1                 # IF MACRO == I-TYPE
    0F84 %Eval_Immediates_Next  ; je %Eval_Immediates_Next    # Skip to next

    # Check for NULL EXPRESSION
    8B42 18                     ; mov_eax,[edx+BYTE] !24      # I->EXPRESSION
    83F8 00                     ; cmp_eax, !0                 # IF NULL == I->EXPRESSION
    0F85 %Eval_Immediates_Next  ; jne %Eval_Immediates_Next   # Skip to next

    # Check if number
    8B42 10                     ; mov_eax,[edx+BYTE] !16      # I->TEXT
    8A18                        ; mov_bl,[eax]                # I->TEXT[0]
    0FB6DB                      ; movzx_ebx,bl                # Extend to use
    83C0 01                     ; add_eax, !1                 # I->TEXT + 1
    8A08                        ; mov_cl,[eax]                # I->TEXT[1]
    0FB6C9                      ; movzx_ecx,cl                # Extend to use
    E8 %numerate_string         ; call %numerate_string       # Convert string to INT
    83F8 00                     ; cmp_eax, !0                 # IF 0 == numerate_number(I->TEXT + 1)
    0F85 %Eval_Immediates_value ; jne %Eval_Immediates_value  # Has a value

    # Last chance for Immediate
    83F9 30                     ; cmp_ecx, !48                # If '0' == I->TEXT[1]
    0F85 %Eval_Immediates_Next  ; jne %Eval_Immediates_Next   # Skip to next

:Eval_Immediates_value
    E8 %express_number          ; call %express_number        # Convert value to hex string
    8942 18                     ; mov_[edx+BYTE],eax !24      # I->EXPRESSION = express_number(value, I-TEXT[0])

:Eval_Immediates_Next
    8B12                        ; mov_edx,[edx]               # I = I->NEXT
    83FA 00                     ; cmp_edx, !0                 # IF NULL == I
    0F85 %Eval_Immediates_Loop  ; jne %Eval_Immediates_Loop   # Otherwise keep looping

    5A                          ; pop_edx                     # Restore EDX
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    58                          ; pop_eax                     # Restore EAX
    C3                          ; ret


# numerate_string function
# Receives CHAR* in EAX
# Returns value of CHAR* in EAX
# Uses EAX for VALUE, EBX for S, ECX for CH and EDI for NEGATIVE?
:numerate_string
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    52                          ; push_edx                    # Protect EDX
    57                          ; push_edi                    # Protect EDI
    89C3                        ; mov_ebx,eax                 # put S in correct place
    B8 00000000                 ; mov_eax, %0                 # Initialize to Zero
:numerate_string_loop
    8A4B 01                     ; mov_cl,[ebx+BYTE] !1        # S[1]
    0FB6C9                      ; movzx_ecx,cl                # make it useful
    83F9 78                     ; cmp_ecx, !120               # IF 'x' == S[1]
    0F84 %numerate_hex          ; je %numerate_hex            # Deal with hex input

    # Assume decimal input
    B9 00000000                 ; mov_ecx, %0                 # Assume no negation
    8A0B                        ; mov_cl,[ebx]                # S[0]
    0FB6C9                      ; movzx_ecx,cl                # make it useful
    83F9 2D                     ; cmp_ecx, !45                # IF '-' == S[0]
    0F85 %numerate_decimal      ; jne %numerate_decimal       # Skip negation

    BF 01000000                 ; mov_edi, %1                 # Set FLAG
    83C3 01                     ; add_ebx, !1                 # S = S + 1

:numerate_decimal
    8A0B                        ; mov_cl,[ebx]                # S[0]
    0FB6C9                      ; movzx_ecx,cl                # make it useful
    83F9 00                     ; cmp_ecx, !0                 # IF NULL == S[0]
    0F84 %numerate_decimal_done ; je %numerate_decimal_done   # We are done

    6BC0 0A                     ; imul_eax, !10               # VALUE = VALUE * 10
    83E9 30                     ; sub_ecx, !48                # CH = CH - '0'
    83F9 09                     ; cmp_ecx, !9                 # Check for illegal
    0F8F %numerate_string_fail  ; jg %numerate_string_fail    # If CH > '9'
    83F9 00                     ; cmp_ecx, !0                 # Check for illegal
    0F8C %numerate_string_fail  ; jl %numerate_string_fail    # IF CH < 0
    01C8                        ; add_eax,ecx                 # VALUE = VALUE + CH
    83C3 01                     ; add_ebx, !1                 # S = S + 1
    E9 %numerate_decimal        ; jmp %numerate_decimal       # Keep looping

:numerate_decimal_done
    83FF 01                     ; cmp_edi, !1                 # Check if need to negate
    0F85 %numerate_string_done  ; jne %numerate_string_done   # Nope

    6BC0 FF                     ; imul_eax, !-1               # VALUE = VALUE * -1
    E9 %numerate_string_done    ; jmp %numerate_string_done   # Done

:numerate_hex
    83C3 02                     ; add_ebx, !2                 # S = S + 2
:numerate_hex_loop
    8A0B                        ; mov_cl,[ebx]                # S[0]
    0FB6C9                      ; movzx_ecx,cl                # make it useful
    83F9 00                     ; cmp_ecx, !0                 # IF NULL == S[0]
    0F84 %numerate_string_done  ; je %numerate_string_done    # We are done

    C1E0 04                     ; shl_eax, !4                 # VALUE = VALUE << 4
    83E9 30                     ; sub_ecx, !48                # CH = CH - '0'
    83F9 0A                     ; cmp_ecx, !10                # IF 10 >= CH
    0F8C %numerate_hex_digit    ; jl %numerate_hex_digit      # NO
    83E9 07                     ; sub_ecx, !7                 # Push A-F into range
:numerate_hex_digit
    83F9 0F                     ; cmp_ecx, !15                # Check for illegal
    0F8F %numerate_string_fail  ; jg %numerate_string_fail    # If CH > 'F'
    83F9 00                     ; cmp_ecx, !0                 # Check for illegal
    0F8C %numerate_string_fail  ; jl %numerate_string_fail    # IF CH < 0
    01C8                        ; add_eax,ecx                 # VALUE = VALUE + CH
    83C3 01                     ; add_ebx, !1                 # S = S + 1
    E9 %numerate_hex_loop       ; jmp %numerate_hex_loop      # Keep looping

:numerate_string_fail
    B8 00000000                 ; mov_eax, %0                 # return ZERO

:numerate_string_done
    5F                          ; pop_edi                     # Restore EDI
    5A                          ; pop_edx                     # Restore EDX
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    C3                          ; ret


# express_number function
# Receives INT in EAX and CHAR in EBX
# Allocates a string and expresses the value in hex
# Returns string in EAX
# Uses EAX for VALUE, EBX for S and ECX for CH
:express_number
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    52                          ; push_edx                    # Protect EDX
    89D9                        ; mov_ecx,ebx                 # Put CH in right place
    89C3                        ; mov_ebx,eax                 # Protect VALUE
    83F9 25                     ; cmp_ecx, !37                # IF '%' == CH
    0F85 %express_number2       ; jne %express_number2        # Otherwise try @

    B8 09000000                 ; mov_eax, %9                 # We need 3 bytes
    E8 %malloc                  ; call %malloc                # Get S pointer
    93                          ; xchg_eax,ebx                # Put S and VALUE in place
    53                          ; push_ebx                    # Protect S
    E8 %hex32l                  ; call %hex32l                # Store 32bits
    E9 %express_number_done     ; jmp %express_number_done    # done

:express_number2
    83F9 40                     ; cmp_ecx, !64                # IF '@' == CH
    0F85 %express_number1       ; jne %express_number1        # Othrewise try !

    B8 05000000                 ; mov_eax, %5                 # We need 3 bytes
    E8 %malloc                  ; call %malloc                # Get S pointer
    93                          ; xchg_eax,ebx                # Put S and VALUE in place
    53                          ; push_ebx                    # Protect S
    E8 %hex16l                  ; call %hex16l                # Store 16bits
    E9 %express_number_done     ; jmp %express_number_done    # done

:express_number1
    B8 03000000                 ; mov_eax, %3                 # We need 3 bytes
    E8 %malloc                  ; call %malloc                # Get S pointer
    93                          ; xchg_eax,ebx                # Put S and VALUE in place
    53                          ; push_ebx                    # Protect S
    E8 %hex8                    ; call %hex8                  # Store 8bit

:express_number_done
    58                          ; pop_eax                     # Restore S
    5A                          ; pop_edx                     # Restore EDX
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    C3                          ; ret


# HEX to ascii routine
# Receives INT in EAX and CHAR* in EBX
# Stores ascii of INT in CHAR*
# Returns only modifying EAX
:hex64l
    50                          ; push_eax                    # Protect top 32
    E8 %hex32l                  ; call %hex32l                # Store it
    58                          ; pop_eax                     # do top 32
    C1E8 20                     ; shr_eax, !32                # do bottom 32 first
:hex32l
    50                          ; push_eax                    # Protect top 16
    E8 %hex16l                  ; call %hex16l                # Store it
    58                          ; pop_eax                     # do top 16
    C1E8 10                     ; shr_eax, !16                # do bottom 16 first
:hex16l
    50                          ; push_eax                    # Protect top byte
    E8 %hex8                    ; call %hex8                  # Store it
    58                          ; pop_eax                     # do high byte
    C1E8 08                     ; shr_eax, !8                 # do bottom byte first
:hex8
    50                          ; push_eax                    # Protect bottom nibble
    C1E8 04                     ; shr_eax, !4                 # do high nibble first
    E8 %hex4                    ; call %hex4                  # Store it
    58                          ; pop_eax                     # do low nibble
:hex4
    83E0 0F                     ; and_eax, !0xF               # isolate nibble
    04 30                       ; add_al, !48                 # convert to ascii
    3C 39                       ; cmp_al, !57                 # valid digit?
    0F86 %hex1                  ; jbe %hex1                   # yes
    04 07                       ; add_al, !7                  # use alpha range
:hex1
    8803                        ; mov_[ebx],al                # store result
    83C3 01                     ; add_ebx, !1                 # next position
    C3                          ; ret


# Preserve_Other function
# Receives List in EAX
# Updates the list in place; does not modify registers
# Uses EAX for I, EBX for I->TEXT
:Preserve_Other
    50                          ; push_eax                    # Protect EAX
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    52                          ; push_edx                    # Protect EDX
:Preserve_Other_Loop
    8B58 18                     ; mov_ebx,[eax+BYTE] !24      # I->EXPRESSION
    83FB 00                     ; cmp_ebx, !0                 # IF NULL == I->EXPRESSION
    0F85 %Preserve_Other_Next   ; jne %Preserve_Other_Next    # Otherwise next

    # Needs preserving
    8B58 10                     ; mov_ebx,[eax+BYTE] !16      # I->TEXT
    8958 18                     ; mov_[eax+BYTE],ebx !24      # I->EXPRESSION = I->TEXT

:Preserve_Other_Next
    8B00                        ; mov_eax,[eax]               # I = I->NEXT
    83F8 00                     ; cmp_eax, !0                 # IF NULL == I
    0F85 %Preserve_Other_Loop   ; jne %Preserve_Other_Loop    # Otherwise keep looping

    5A                          ; pop_edx                     # Restore EDX
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    58                          ; pop_eax                     # Restore EAX
    C3                          ; ret


# Print_Hex function
# Receives list in EAX
# walks the list and prints the I->EXPRESSION for all nodes followed by newline
# Uses EBX for I
:Print_Hex
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    89EB                        ; mov_ebx,ebp                 # I = Head
:Print_Hex_Loop
    8B43 08                     ; mov_eax,[ebx+BYTE] !8       # I->TYPE
    83F8 01                     ; cmp_eax, !1                 # IF MACRO == I->TYPE
    0F84 %Print_Hex_Next        ; je %Print_Hex_Next          # Skip

    8B43 18                     ; mov_eax,[ebx+BYTE] !24      # Using EXPRESSION
    E8 %File_Print              ; call %File_Print            # Print it
    B8 0A000000                 ; mov_eax, %10                # NEWLINE
    E8 %fputc                   ; call %fputc                 # Append it

:Print_Hex_Next
    8B1B                        ; mov_ebx,[ebx]               # Iterate to next Token
    83FB 00                     ; cmp_ebx, !0                 # Check for NULL
    0F85 %Print_Hex_Loop        ; jne %Print_Hex_Loop         # Otherwise keep looping

    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    C3                          ; ret


# File_Print function
# Receives CHAR* in EAX
# calls fputc for every non-null char
:File_Print
    53                          ; push_ebx                    # Protect EBX
    51                          ; push_ecx                    # Protect ECX
    89C3                        ; mov_ebx,eax                 # Protect S
    83F8 00                     ; cmp_eax, !0                 # Protect against nulls
    0F84 %File_Print_Done       ; je %File_Print_Done         # Simply don't try to print them
:File_Print_Loop
    8A03                        ; mov_al,[ebx]                # Read byte
    0FB6C0                      ; movzx_eax,al                # zero extend
    83F8 00                     ; cmp_eax, !0                 # Check for NULL
    0F84 %File_Print_Done       ; je %File_Print_Done         # Stop at NULL

    E8 %fputc                   ; call %fputc                 # write it
    83C3 01                     ; add_ebx, !1                 # S = S + 1
    E9 %File_Print_Loop         ; jmp %File_Print_Loop        # Keep going

:File_Print_Done
    59                          ; pop_ecx                     # Restore ECX
    5B                          ; pop_ebx                     # Restore EBX
    C3                          ; ret


# fputc function
# receives CHAR in EAX and FILE* in [Output]
# writes char and returns
:fputc
    52                          ; push_edx                    # Protect EDX
    51                          ; push_ecx                    # protect ECX
    53                          ; push_ebx                    # protect EBX
    50                          ; push_eax                    # We are writing eax
    8D0C24                      ; lea_ecx,[esp]               # Get stack address
    8B1D &Output                ; mov_ebx,[DWORD] &Output     # Write to target file
    B8 04000000                 ; mov_eax, %4                 # the syscall number for write
    BA 01000000                 ; mov_edx, %1                 # set the size of chars we want
    CD 80                       ; int !0x80                   # call the Kernel
    58                          ; pop_eax                     # Restore stack
    5B                          ; pop_ebx                     # Restore EBX
    59                          ; pop_ecx                     # Restore ECX
    5A                          ; pop_edx                     # Restore EDX
    C3                          ; ret

:Output
    00000000                    ; %0
:Input
    00000000                    ; %0
:ELF_end

File /x86/cc_x86.M1

Live-bootstrap source file is 'seed/stage0-posix/x86/cc_x86.M1'.
URL: https://github.com/oriansj/stage0-posix-x86/blob/83508012c952e3aa3f70a89fd7d85d9a1af6f305/cc_x86.M1
# SPDX-FileCopyrightText: © 2017 Jeremiah Orians
#
# SPDX-License-Identifier: GPL-3.0-or-later

DEFINE add_eax, 83C0
DEFINE add_ebx, 83C3
DEFINE add_ecx, 83C1
DEFINE add_edx, 83C2
DEFINE add_esi, 83C6
DEFINE add_eax,ebx 01D8
DEFINE add_eax,ecx 01C8
DEFINE add_ebx,eax 01C3
DEFINE add_ecx,edi 01F9
DEFINE and_eax,ebx 21D8
DEFINE and_eax, 25
DEFINE call_eax FFD0
DEFINE call E8
DEFINE cmp_eax, 83F8
DEFINE cmp_ebp, 83FD
DEFINE cmp_ebx, 83FB
DEFINE cmp_ecx, 83F9
DEFINE cmp_edx, 83FA
DEFINE cmp_esi, 83FE
DEFINE cmp_eax,ebx 39D8
DEFINE cmp_eax,ecx 39C8
DEFINE cmp_ebx,ecx 39CB
DEFINE cmp_ebx,edx 39D3
DEFINE cmp_esi,edi 39FE
DEFINE idiv_ebx F7FB
DEFINE imul_eax, 6BC0
DEFINE imul_ebp, 6BED
DEFINE imul_eax,ebx 0FAFC3
DEFINE int CD
DEFINE jbe8 76
DEFINE je 0F84
DEFINE jg 0F8F
DEFINE jl 0F8C
DEFINE jle 0F8E
DEFINE jmp E9
DEFINE jne 0F85
DEFINE lea_ecx,[esp] 8D0C24
DEFINE mov_eax, B8
DEFINE mov_ebx, BB
DEFINE mov_ecx, B9
DEFINE mov_edi, BF
DEFINE mov_edx, BA
DEFINE mov_esi, BE
DEFINE mov_eax,ebp 89E8
DEFINE mov_eax,ebx 89D8
DEFINE mov_eax,ecx 89C8
DEFINE mov_eax,edx 89D0
DEFINE mov_eax,esi 89F0
DEFINE mov_ebp,eax 89C5
DEFINE mov_ebp,edx 89D5
DEFINE mov_ebx,eax 89C3
DEFINE mov_ebx,ecx 89CB
DEFINE mov_ebx,edx 89D3
DEFINE mov_ecx,eax 89C1
DEFINE mov_ecx,ebx 89D9
DEFINE mov_edi,esi 89F7
DEFINE mov_edx,eax 89C2
DEFINE mov_edx,ebx 89DA
DEFINE mov_esi,eax 89C6
DEFINE mov_esi,edi 89FE
DEFINE mov_eax,[DWORD] A1
DEFINE mov_ebx,[DWORD] 8B1D
DEFINE mov_ecx,[DWORD] 8B0D
DEFINE mov_al,[eax] 8A00
DEFINE mov_al,[ebx] 8A03
DEFINE mov_al,[ecx] 8A01
DEFINE mov_al,[edx] 8A02
DEFINE mov_bl,[ebx] 8A1B
DEFINE mov_bl,[ecx] 8A19
DEFINE mov_bl,[edx] 8A1A
DEFINE mov_cl,[ebx] 8A0B
DEFINE mov_cl,[ebx+BYTE] 8A4B
DEFINE mov_eax,[eax] 8B00
DEFINE mov_eax,[ebx] 8B03
DEFINE mov_eax,[eax+BYTE] 8B40
DEFINE mov_eax,[ebp+BYTE] 8B45
DEFINE mov_eax,[ebx+BYTE] 8B43
DEFINE mov_eax,[ecx+BYTE] 8B41
DEFINE mov_eax,[edx+BYTE] 8B42
DEFINE mov_ebp,[ebp] 8B6D00
DEFINE mov_ebx,[eax+BYTE] 8B58
DEFINE mov_ebx,[ebx] 8B1B
DEFINE mov_ebx,[ebx+BYTE] 8B5B
DEFINE mov_ebx,[ecx+BYTE] 8B59
DEFINE mov_ecx,[eax+BYTE] 8B48
DEFINE mov_ecx,[ebx] 8B0B
DEFINE mov_ecx,[ecx] 8B09
DEFINE mov_ecx,[ecx+BYTE] 8B49
DEFINE mov_ecx,[edx+BYTE] 8B4A
DEFINE mov_edi,[edx+BYTE] 8B7A
DEFINE mov_edx,[edx+BYTE] 8B52
DEFINE mov_[DWORD],eax A3
DEFINE mov_[DWORD],ebx 891D
DEFINE mov_[DWORD],ecx 890D
DEFINE mov_[DWORD],edx 8915
DEFINE mov_[ebx],al 8803
DEFINE mov_[ecx],al 8801
DEFINE mov_[esi],al 8806
DEFINE mov_[ecx],bl 8819
DEFINE mov_[eax],ebx 8918
DEFINE mov_[eax],ecx 8908
DEFINE mov_[eax+BYTE],ebx 8958
DEFINE mov_[eax+BYTE],ecx 8948
DEFINE mov_[eax+BYTE],edx 8950
DEFINE mov_[ebp+BYTE],eax 8945
DEFINE mov_[ebp+BYTE],edx 8955
DEFINE mov_[ebp+BYTE],esi 8975
DEFINE mov_[ebx],eax 8903
DEFINE mov_[ecx+BYTE],eax 8941
DEFINE mov_[edx],eax 8902
DEFINE mov_[edx+BYTE],eax 8942
DEFINE mov_[edx+BYTE],ebp 896A
DEFINE mov_[edx+BYTE],ebx 895A
DEFINE mov_[edx+BYTE],ecx 894A
DEFINE mov_[edx+BYTE],esi 8972
DEFINE movzx_eax,al 0FB6C0
DEFINE movzx_ebx,bl 0FB6DB
DEFINE movzx_ecx,cl 0FB6C9
DEFINE NULL 00000000
DEFINE pop_eax 58
DEFINE pop_ebp 5D
DEFINE pop_ebx 5B
DEFINE pop_ecx 59
DEFINE pop_edi 5F
DEFINE pop_edx 5A
DEFINE pop_esi 5E
DEFINE push_eax 50
DEFINE push_ebp 55
DEFINE push_ebx 53
DEFINE push_ecx 51
DEFINE push_edi 57
DEFINE push_edx 52
DEFINE push_esi 56
DEFINE ret C3
DEFINE sal_eax, C1E0
DEFINE shr_eax, C1E8
DEFINE shr_ebx, C1EB
DEFINE sub_eax, 83E8
DEFINE sub_ecx, 83E9
DEFINE sub_esi, 83EE
DEFINE xchg_eax,ebx 93


    # Register usage:
    # EAX => Temps

    # Struct TYPE format: (size 28)
    # NEXT => 0
    # SIZE => 4
    # OFFSET => 8
    # INDIRECT => 12
    # MEMBERS => 16
    # TYPE => 20
    # NAME => 24

    # Struct TOKEN_LIST format: (size 20)
    # NEXT => 0
    # LOCALS/PREV => 4
    # S => 8
    # TYPE => 12
    # ARGS/DEPTH => 16

# Where the ELF Header is going to hit
# Simply jump to _start
# Our main function
:_start
    pop_eax                                     # Get the number of arguments
    pop_ebx                                     # Get the program name
    pop_ebx                                     # Get the actual input name
    mov_ecx, %0                                 # prepare read_only
    mov_eax, %5                                 # the syscall number for open()
    int !0x80                                   # Now open that damn file
    mov_[DWORD],eax &Input_file                 # Preserve the file pointer we were given

    pop_ebx                                     # Get the actual output name
    mov_ecx, %577                               # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    mov_edx, %384                               # Prepare file as RW for owner only (600 in octal)
    mov_eax, %5                                 # the syscall number for open()
    int !0x80                                   # Now open that damn file
    cmp_eax, !0                                 # Check for missing output
    jg %_start_out                              # Have real input
    mov_eax, %1                                 # Use stdout

:_start_out
    mov_[DWORD],eax &Output_file                # Preserve the file pointer we were given

    mov_eax, %45                                # the Syscall # for SYS_BRK
    mov_ebx, %0                                 # Get current brk
    int !0x80                                   # Let the kernel do the work
    mov_[DWORD],eax &MALLOC                     # Set our malloc pointer
    mov_eax, %0                                 # HEAD = NULL
    call %read_all_tokens                       # Read all tokens
    call %Reverse_List                          # Reverse order
#   call %debug_list                            # Try to figure out what is wrong
    mov_[DWORD],eax &global_token               # Set global_token
    call %program                               # Convert into program
    mov_eax, &header_string1                    # Our header string
    call %File_Print                            # Print it
    mov_eax,[DWORD] &output_list                # Our output_list
    call %recursive_output                      # Print core program
#   mov_eax, &header_string2                    # Our Enable debug
#   call %File_Print                            # Print it
    mov_eax, &header_string3                    # Our second label
    call %File_Print                            # Print it
    mov_eax,[DWORD] &globals_list               # Our globals
    call %recursive_output                      # Get them
    mov_eax, &header_string4                    # Our final header
    call %File_Print                            # Print it
    mov_eax,[DWORD] &strings_list               # Our strings
    call %recursive_output                      # Get them
    mov_eax, &header_string5                    # Make this a bare assembly
    call %File_Print                            # Print it

:Done
    # program completed Successfully
    mov_ebx, %0                                 # All is well
    mov_eax, %1                                 # put the exit syscall number in eax
    int !0x80                                   # Call it a good day

:header_string1  "
# Core program
"
:header_string2  "
:ELF_data
"
:header_string3  "
# Program global variables
"
:header_string4  "
# Program strings
"
:header_string5  "
:ELF_end
"


# read_all_tokens function
# Receives Token_List* in EAX
# Tokenizes all input and returns updated list in EAX
# Returns TOKEN in EAX
# Uses EAX for C
:read_all_tokens
    mov_[DWORD],eax &Token
    call %fgetc
:read_all_tokens_loop
    cmp_eax, !-4                                # Check for EOF
    je %read_all_tokens_done                    # Stop if found
    call %get_token                             # Read all tokens
    jmp %read_all_tokens_loop                   # Loop
:read_all_tokens_done
    mov_eax,[DWORD] &Token
    ret


# get_token function
# Receives INT in EAX
# Makes a list of TOKEN_LIST
# C and STRING_INDEX are stored in memory, ECX is used for S and EDX is used for current
# Returns C in EAX
:get_token
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX

    mov_[DWORD],eax &C                          # Set C

    mov_eax, %20                                # Malloc CURRENT
    call %malloc                                # Get Pointer
    mov_edx,eax                                 # Set CURRENT

    mov_eax, %256                               # Malloc the string
    call %malloc                                # Get pointer to S
    mov_ecx,eax                                 # Set S
    mov_[edx+BYTE],ecx !8                       # CURRENT->S = S
:reset
    mov_[DWORD],ecx &string_index               # S[0]
    mov_eax,[DWORD] &C                          # Using C

    call %clear_white_space                     # Clear WhiteSpace
    mov_[DWORD],eax &C                          # Set C

    cmp_eax, !-4                                # Check for EOF
    je %get_token_abort                         # if EOF abort

    cmp_eax, !35                                # Check for '#'
    jne %get_token_alpha                        # Nope

    # Deal with # line comments
    call %purge_macro                           # Let it handle it
    mov_[DWORD],eax &C                          # Set C
    jmp %reset                                  # Try again

:get_token_alpha
    mov_eax,[DWORD] &C                          # Send C
    mov_ebx, &alphas                            # Get alphanumerics
    call %In_Set                                # See if in set
    cmp_eax, !1                                 # IF TRUE
    jne %get_token_symbol                       # Otherwise

    # Store keywords
    mov_eax,[DWORD] &C                          # Send C
    call %preserve_keyword                      # Store
    mov_[DWORD],eax &C                          # Set C
    jmp %get_token_done                         # Be done with this token

:get_token_symbol
    mov_eax,[DWORD] &C                          # Send C
    mov_ebx, &symbols                           # Get symbols
    call %In_Set                                # See if in set
    cmp_eax, !1                                 # IF TRUE
    jne %get_token_strings                      # Otherwise

    # Store symbols
    mov_eax,[DWORD] &C                          # Send C
    call %preserve_symbol                       # Store
    mov_[DWORD],eax &C                          # Set C
    jmp %get_token_done                         # Be done with this token

:get_token_strings
    mov_eax,[DWORD] &C                          # Send C
    mov_ebx, &strings                           # Get strings
    call %In_Set                                # See if in set
    cmp_eax, !1                                 # IF TRUE
    jne %get_token_comment                      # Otherwise

    # Store String
    mov_eax,[DWORD] &C                          # Send C
    call %consume_word                          # Store
    mov_[DWORD],eax &C                          # Set C
    jmp %get_token_done                         # Be done with this token

:get_token_comment
    mov_eax,[DWORD] &C                          # Send C
    cmp_eax, !47                                # IF '/' == C
    jne %get_token_else                         # Otherwise

    call %consume_byte                          # Hope it just is '/'
    mov_[DWORD],eax &C                          # Set C

    cmp_eax, !42                                # IF '*' we have '/*'
    jne %get_token_comment_line                 # Check for '//'

    # Deal with /* block comments */
    call %fgetc                                 # get next C
    mov_[DWORD],eax &C                          # Set C
:get_token_comment_block_outer
    mov_eax,[DWORD] &C                          # Using C
    cmp_eax, !47                                # IF '/' != C
    je %get_token_comment_block_done            # be done

:get_token_comment_block_inner
    mov_eax,[DWORD] &C                          # Using C
    cmp_eax, !42                                # IF '*' != C
    je %get_token_comment_block_iter            # jump over

    # Deal with inner loop
    call %fgetc                                 # get next C
    mov_[DWORD],eax &C                          # Set C
    jmp %get_token_comment_block_inner          # keep going

:get_token_comment_block_iter
    call %fgetc                                 # get next C
    mov_[DWORD],eax &C                          # Set C
    jmp %get_token_comment_block_outer

:get_token_comment_block_done
    call %fgetc                                 # get next C
    mov_[DWORD],eax &C                          # Set C
    jmp %reset                                  # throw away, try again

:get_token_comment_line
    cmp_eax, !47                                # IF '/' we have //
    jne %get_token_done                         # keep if just '/'

    # Deal with // line comment
    call %fgetc                                 # drop to match
    mov_[DWORD],eax &C                          # Set C
    jmp %reset                                  # throw away, try again

:get_token_else
    mov_eax,[DWORD] &C                          # Send C
    call %consume_byte
    mov_[DWORD],eax &C                          # Set C

:get_token_done
    mov_eax,[DWORD] &Token                      # TOKEN
    mov_[edx+BYTE],eax !4                       # CURRENT->PREV = TOKEN
    mov_[edx],eax                               # CURRENT->NEXT = TOKEN
    mov_[DWORD],edx &Token                      # TOKEN = CURRENT

:get_token_abort
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    mov_eax,[DWORD] &C                          # Return C
    ret


# Malloc isn't actually required if the program being built fits in the initial memory
# However, it doesn't take much to add it.
# Requires [MALLOC] to be initialized and EAX to have the number of desired bytes
:malloc
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    mov_ebx,[DWORD] &MALLOC                     # Using the current pointer
    add_ebx,eax                                 # Request the number of desired bytes
    mov_eax, %45                                # the Syscall # for SYS_BRK
    int !0x80                                   # call the Kernel
    mov_eax,[DWORD] &MALLOC                     # Return pointer
    mov_[DWORD],ebx &MALLOC                     # Update pointer
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# clear_white_space function
# Receives INT C in EAX
# Returns first non-whitespace char in EAX
:clear_white_space
    cmp_eax, !32                                # Check for ' '
    je %clear_white_space_wipe                  # wipe it out

    cmp_eax, !10                                # Check for '\n'
    je %clear_white_space_wipe                  # wipe it output

    cmp_eax, !9                                 # Check for '\t'
    jne %clear_white_space_done                 # looks like non-whitespace

:clear_white_space_wipe
    call %fgetc                                 # Read a new byte
    cmp_eax, !-4                                # Check for EOF
    je %clear_white_space_done                  # Short circuit
    jmp %clear_white_space                      # iterate

:clear_white_space_done
    ret


# In_Set function
# Receives Char C in EAX and CHAR* in EBX
# Returns 1 if true, zero if false in EAX
:In_Set
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
:In_Set_loop
    mov_cl,[ebx]                                # Read char
    movzx_ecx,cl                                # Zero extend it

    cmp_eax,ecx                                 # See if they match
    je %In_Set_True                             # return true

    cmp_ecx, !0                                 # Check for NULL
    je %In_Set_False                            # return false

    add_ebx, !1                                 # s = s + 1
    jmp %In_Set_loop                            # Keep looping

:In_Set_True
    mov_eax, %1                                 # Set True
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:In_Set_False
    mov_eax, %0                                 # Set FALSE
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:alphas  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
:symbols  "<=>|&!-"
:strings  '22 27 00'


# purge_macro function
# Receives CH in EAX
# Reads chars until Line feed is read
# returns line feed
:purge_macro
    call %fgetc                                 # read next char
    cmp_eax, !10                                # Check for '\n'
    jne %purge_macro                            # Keep going
    ret


# preserve_keyword function
# Receives INT C in EAX
# collects all chars in keyword
# Returns C in EAX
# Uses ECX for INT C
:preserve_keyword
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ecx,eax                                 # Setup C
    mov_ebx, &alphas                            # Concerning ourselves with "abc.."
:preserve_keyword_loop
    call %In_Set                                # Check if alphanumerics
    cmp_eax, !1                                 # IF TRUE
    jne %preserve_keyword_label                 # Otherwise check for label

    mov_eax,ecx                                 # Pass C
    call %consume_byte                          # consume that byte
    mov_ecx,eax                                 # Update C
    jmp %preserve_keyword_loop                  # keep looping

:preserve_keyword_label
    mov_eax,ecx                                 # Fix return
    cmp_eax, !58                                # Check for ':'
    jne %preserve_keyword_done                  # be done

    # Fix our goto label
    call %fixup_label                           # Fix the label
    mov_eax, %32                                # Return Whitespace

:preserve_keyword_done
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# preserve_symbol function
# Receives INT C in EAX
# collects all chars in symbol
# Returns C in EAX
# Uses ECX for INT C
:preserve_symbol
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ecx,eax                                 # Setup C
    mov_ebx, &symbols                           # Concerning ourselves with "<=>.."
:preserve_symbol_loop
    call %In_Set                                # Check if symbol
    cmp_eax, !1                                 # IF TRUE
    jne %preserve_symbol_done                   # Otherwise be done

    mov_eax,ecx                                 # Pass C
    call %consume_byte                          # consume that byte
    mov_ecx,eax                                 # Update C
    jmp %preserve_symbol_loop                   # keep looping

:preserve_symbol_done
    mov_eax,ecx                                 # Fix return
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# consume_word function
# receives INT C in EAX
# returns INT C in EAX
# Uses EAX for C, EBX for FREQ and ECX for ESCAPE
:consume_word
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ebx,eax                                 # FREQ = C
    mov_ecx, %0                                 # ESCAPE = FALSE
:consume_word_loop
    cmp_ecx, !0                                 # IF !ESCAPE
    jne %consume_word_escape                    # Enable escape

    cmp_eax, !92                                # if '\\'
    jne %consume_word_iter                      # keep state

    mov_ecx, %1                                 # ESCAPE = TRUE
    jmp %consume_word_iter                      # keep going

:consume_word_escape
    mov_ecx, %0                                 # ESCAPE = FALSE

:consume_word_iter
    call %consume_byte                          # read next char

    cmp_ecx, !0                                 # IF ESCAPE
    jne %consume_word_loop                      # keep looping

    cmp_eax,ebx                                 # IF C != FREQ
    jne %consume_word_loop                      # keep going

    call %fgetc                                 # return next char
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# consume_byte function
# Receives INT C in EAX
# Inserts C into string S, updates String S
# Returns Next char in EAX
:consume_byte
    push_ebx                                    # Protect EBX
    mov_ebx,[DWORD] &string_index               # S[0]
    mov_[ebx],al                                # S[0] = C
    add_ebx, !1                                 # S = S + 1
    mov_[DWORD],ebx &string_index               # Update S
    call %fgetc
    pop_ebx                                     # Restore EBX
    ret


# fixup_label function
# Receives S in ECX
# prepends ':' to string and returns registers unchanged
# Uses EAX for HOLD, EBX for PREV and ECX for S[0]
:fixup_label
    push_eax                                    # Protect EAX
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_eax, %58                                # HOLD = ':'
    mov_ecx,[edx+BYTE] !8                       # HOLD_STRING[0]
:fixup_label_loop
    mov_ebx,eax                                 # PREV = HOLD
    mov_al,[ecx]                                # HOLD = HOLD_STRING[I]
    movzx_eax,al                                # make useful
    mov_[ecx],bl                                # HOLD_STRING[I] = PREV
    add_ecx, !1                                 # I = I + 1
    cmp_eax, !0                                 # IF NULL == HOLD
    jne %fixup_label_loop                       # Keep looping

    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    pop_eax                                     # Restore EAX
    ret


# fgetc function
# Loads FILE* from [INPUT_FILE]
# Returns -4 (EOF) or char in EAX
:fgetc
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    mov_eax, %-4                                # Put EOF in eax
    push_eax                                    # Assume bad (If nothing read, value will remain EOF)
    lea_ecx,[esp]                               # Get stack address
    mov_ebx,[DWORD] &Input_file                 # Where are we reading from
    mov_eax, %3                                 # the syscall number for read
    mov_edx, %1                                 # set the size of chars we want
    int !0x80                                   # call the Kernel
    pop_eax                                     # Get either char or EOF
    cmp_eax, !-4                                # Check for EOF
    je %fgetc_done                              # Return as is
    movzx_eax,al                                # Make it useful
:fgetc_done
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# Reverse_List function
# Receives List in EAX
# Returns the list reversed in EAX
:Reverse_List
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ebx,eax                                 # Set HEAD
    mov_eax, %0                                 # ROOT = NULL
:Reverse_List_Loop
    cmp_ebx, !0                                 # WHILE HEAD != NULL
    je %Reverse_List_Done                       # Stop otherwise

    mov_ecx,[ebx]                               # NEXT = HEAD->NEXT
    mov_[ebx],eax                               # HEAD->NEXT = ROOT
    mov_eax,ebx                                 # ROOT = HEAD
    mov_ebx,ecx                                 # HEAD = NEXT
    jmp %Reverse_List_Loop                      # Keep Going

:Reverse_List_Done
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# recursive_output function
# Receives list in EAX
# walks the list and prints the I->S for all nodes backwards
# Uses EBX for I
:recursive_output
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    cmp_eax, !0                                 # Check for NULL
    je %recursive_output_done                   # Skip the work
    mov_ebx,eax                                 # I = Head

    mov_eax,[ebx]                               # Iterate to next Token
    call %recursive_output                      # Recurse

    mov_eax,[ebx+BYTE] !8                       # Using S
    call %File_Print                            # Print it

:recursive_output_done
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# File_Print function
# Receives CHAR* in EAX
# calls fputc for every non-null char
:File_Print
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ebx,eax                                 # Protect S
    cmp_eax, !0                                 # Protect against nulls
    je %File_Print_Done                         # Simply don't try to print them
:File_Print_Loop
    mov_al,[ebx]                                # Read byte
    movzx_eax,al                                # zero extend
    cmp_eax, !0                                 # Check for NULL
    je %File_Print_Done                         # Stop at NULL

    call %fputc                                 # write it
    add_ebx, !1                                 # S = S + 1
    jmp %File_Print_Loop                        # Keep going

:File_Print_Done
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# fputc function
# receives CHAR in EAX and load FILE* from [OUTPUT_FILE]
# writes char and returns
:fputc
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    push_eax                                    # We are writing eax
    lea_ecx,[esp]                               # Get stack address
    mov_ebx,[DWORD] &Output_file                # Write to target file
    mov_eax, %4                                 # the syscall number for write
    mov_edx, %1                                 # set the size of chars we want
    int !0x80                                   # call the Kernel
    pop_eax                                     # Restore stack
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# program function
# receives nothing, returns nothing
# Uses EAX for type_size
:program
    # The binary initialized the globals to null, so we can skip those steps
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX

:new_type
    mov_eax,[DWORD] &global_token               # Using global_token
    cmp_eax, !0                                 # Check if NULL
    je %program_done                            # Be done if null

    mov_ebx,[eax+BYTE] !8                       # GLOBAL_TOKEN->S
    mov_eax, &constant                          # "CONSTANT"
    call %match                                 # IF GLOBAL_TOKEN->S == "CONSTANT"
    cmp_eax, !0                                 # If true
    jne %program_else                           # Looks like not a constant

    # Deal with minimal constant case
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->next
    mov_[DWORD],eax &global_token               # global_token = global_token->next

    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_ebx, %0                                 # NULL
    mov_ecx,[DWORD] &global_constant_list       # global_constant_list
    call %sym_declare                           # Declare that constant
    mov_[DWORD],eax &global_constant_list       # global_constant_list = sym_declare(global_token->s, NULL, global_constant_list);

    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx]                               # global_token->next
    mov_[eax+BYTE],ebx !16                      # global_constant_list->arguments = global_token->next

    mov_ebx,[ebx]                               # global_token->next->next
    mov_[DWORD],ebx &global_token               # global_token = global_token->next->next
    jmp %new_type                               # go around again

:program_else
    call %type_name                             # Figure out the type_size
    cmp_eax, !0                                 # IF NULL == type_size
    je %new_type                                # it was a new type

    # Add to global symbol table
    mov_ebx,eax                                 # put type_size in the right spot
    mov_eax,[DWORD] &global_token               # Using global token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_ecx,[DWORD] &global_symbol_list         # Using global_symbol_list
    call %sym_declare                           # Declare symbol
    mov_[DWORD],eax &global_symbol_list         # global_symbol_list = sym_declare(global_token->s, type_size, global_symbol_list);
    mov_ebx,[DWORD] &global_token               # Using global token
    mov_ebx,[ebx]                               # global_token->next
    mov_[DWORD],ebx &global_token               # global_token = global_token->next

    mov_ebx,[DWORD] &global_token               # Using global token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &semicolon                         # ";"
    call %match                                 # if(match(";", global_token->s))
    cmp_eax, !0                                 # If true
    jne %program_function                       # looks like not a match

    # Deal with the global variable
    mov_ebx,[DWORD] &globals_list               # Using globals_list
    mov_eax, &program_string_0                  # ":GLOBAL_"
    call %emit                                  # Emit it
    mov_ebx,eax                                 # update globals_list

    mov_eax,[DWORD] &global_token               # Using global token
    mov_eax,[eax+BYTE] !4                       # global token->prev
    mov_eax,[eax+BYTE] !8                       # global token->prev->s
    call %emit                                  # Emit it

    mov_ebx,eax                                 # update globals_list
    mov_eax, &program_string_1                  # "\nNULL\n"
    call %emit                                  # Emit it
    mov_[DWORD],eax &globals_list               # update globals_list

    mov_eax,[DWORD] &global_token               # Using global token
    mov_eax,[eax]                               # global_token->next
    mov_[DWORD],eax &global_token               # global_token = global_token->next
    jmp %new_type                               # go around again

:program_function
    mov_ebx,[DWORD] &global_token               # Using global token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &open_paren                        # "("
    call %match                                 # if(match(";", global_token->s))
    cmp_eax, !0                                 # If true
    jne %program_error                          # Otherwise deal with error case

    # Deal with function definition
    call %declare_function                      # Lets get the parsing rolling
    jmp %new_type                               # Keep looping through functions

:program_error
    # Deal with the case of something we don't support
    # NOT IMPLEMENTED

:program_done
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

# Strings needed by the program function
:program_string_0  ":GLOBAL_"
:program_string_1  "
NULL
"


# declare_function function
# Receives nothing and returns nothing
# Sets current function and adds it to the global function list
:declare_function
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_eax, %0                                 # Using NULL
    mov_[DWORD],eax &current_count              # current_count = 0

    mov_eax,[DWORD] &global_token               # Using global token
    mov_eax,[eax+BYTE] !4                       # global token->prev
    mov_eax,[eax+BYTE] !8                       # global token->prev->s
    mov_ebx, %0                                 # NULL
    mov_ecx,[DWORD] &global_function_list       # global_function_list
    call %sym_declare                           # sym_declare(global_token->prev->s, NULL, global_function_list);
    mov_[DWORD],eax &function                   # function = sym_declare(global_token->prev->s, NULL, global_function_list);
    mov_[DWORD],eax &global_function_list       # global_function_list = function

    call %collect_arguments                     # collect all of the function arguments

    mov_eax,[DWORD] &global_token               # Using global token
    mov_eax,[eax+BYTE] !8                       # global token->s
    mov_ebx, &semicolon                         # ";"
    call %match                                 # IF global token->s == ";"
    cmp_eax, !0                                 # If true
    jne %declare_function_full                  # It was a prototype

    # Deal with prototypes
    mov_eax,[DWORD] &global_token               # Using global token
    mov_eax,[eax]                               # global token->next
    mov_[DWORD],eax &global_token               # global token = global token->next
    jmp %declare_function_done                  # Move on

:declare_function_full
    # Deal with full function definitions
    mov_eax, &declare_function_string_0         # "# Defining function "
    call %emit_out                              # emit it

    mov_eax,[DWORD] &function                   # function
    mov_eax,[eax+BYTE] !8                       # function->s
    call %emit_out                              # emit it

    mov_eax, &declare_function_string_1         # "\n:FUNCTION_"
    call %emit_out                              # emit it

    mov_eax,[DWORD] &function                   # function
    mov_eax,[eax+BYTE] !8                       # function->s
    call %emit_out                              # emit it

    mov_eax, &declare_function_string_3         # "\n"
    call %emit_out                              # emit it

    call %statement                             # Recursively get the function pieces

    mov_eax,[DWORD] &output_list                # output
    mov_eax,[eax+BYTE] !8                       # output->s
    mov_ebx, &declare_function_string_2         # "ret\n"
    call %match                                 # IF output->s == "ret\n"
    cmp_eax, !0                                 # If true we can skip adding it
    je %declare_function_done                   # otherwise we need to add it

    # Add the return to the end of a function lacking a return;
    mov_eax, &declare_function_string_2         # "ret\n"
    call %emit_out                              # emit it

:declare_function_done
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:declare_function_string_0  "# Defining function "
:declare_function_string_1  "
:FUNCTION_"
:declare_function_string_2  "ret
"
:declare_function_string_3  "
"


# collect_arguments function
# Receives nothing
# Returns Nothing
# Adds arguments to the function definition
# holds struct type* type_size in ECX, then replace with struct token_list* a in ECX when type_size is used
:collect_arguments
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->next
    mov_[DWORD],eax &global_token               # global_token = global_token->next
:collect_arguments_loop
    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &close_paren                       # ")"
    call %match                                 # IF global_token->S == ")"
    cmp_eax, !0                                 # we reached the end
    je %collect_arguments_done                  # be done

    # deal with the case of there are arguments
    call %type_name                             # Get the type
    mov_ecx,eax                                 # put type_size safely out of the way

    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &close_paren                       # ")"
    call %match                                 # IF global_token->S == ")"
    cmp_eax, !0                                 # is a foo(int, char,void) case
    je %collect_arguments_common                # deal with commas

    # Trying second else
    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &comma                             # ","
    call %match                                 # IF global_token->S == ","
    cmp_eax, !0                                 # then deal with the common
    je %collect_arguments_common                # case of commas between arguments

    # deal with foo(int a, char b)
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_ebx,ecx                                 # put type_size in the right place
    mov_ecx,[DWORD] &function                   # Using function
    mov_ecx,[ecx+BYTE] !16                      # function->args
    call %sym_declare                           # sym_declare(global_token->s, type_size, function->arguments);
    mov_ecx,eax                                 # put a in a safe place

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !16                      # function->args
    cmp_eax, !0                                 # IF function->args == NULL
    jne %collect_arguments_another              # otherwise it isn't the first

    # Deal with the case of first argument in the function
    mov_eax, %-4                                # -4
    mov_[ecx+BYTE],eax !16                      # a->depth = -4
    jmp %collect_arguments_next                 # get to next

:collect_arguments_another
    # deal with the case of non-first arguments
    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !16                      # function->args
    mov_eax,[eax+BYTE] !16                      # function->args->depth
    sub_eax, !4                                 # function->args->depth - 4
    mov_[ecx+BYTE],eax !16                      # a->depth = function->args->depth - 4

:collect_arguments_next
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->next
    mov_[DWORD],eax &global_token               # global_token = global_token->next

    mov_eax,[DWORD] &function                   # Using function
    mov_[eax+BYTE],ecx !16                      # function->args = a

:collect_arguments_common
    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &comma                             # ","
    call %match                                 # IF global_token->S == ","
    cmp_eax, !0                                 # then deal with the comma
    jne %collect_arguments_loop                 # otherwise loop

    # keep foo(bar(), 1) expressions working
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->next
    mov_[DWORD],eax &global_token               # global_token = global_token->next
    jmp %collect_arguments_loop                 # keep going

:collect_arguments_done
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->next
    mov_[DWORD],eax &global_token               # global_token = global_token->next
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# statement function
# Receives nothing
# Returns nothing
# Walks down global_token recursively to collect the contents of the function
:statement
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX

    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &open_curly_brace                  # "{"
    call %match                                 # IF global_token->S == "{"
    jne %statement_label                        # otherwise try label

    # deal with { statement }
    call %recursive_statement                   # Statements inside of statements for days
    jmp %statement_done                         # Be done

:statement_label
    mov_al,[ebx]                                # global_token->S[0]
    movzx_eax,al                                # make it useful
    cmp_eax, !58                                # IF global_token->S == ':'
    jne %statement_local                        # otherwise try locals

    # deal with labels
    mov_eax,ebx                                 # put global_token->S in the right spot
    call %emit_out                              # emit it

    mov_eax, &statement_string_0                # Using "\t#C goto label\n"
    call %emit_out                              # emit it

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->next
    mov_[DWORD],eax &global_token               # global_token = global_token->next
    jmp %statement_done                         # be done

:statement_local
    mov_eax,ebx                                 # put global_token->S in the right place
    mov_ebx, &prim_types                        # pointer to primitive types
    call %lookup_type                           # See if found
    cmp_eax, !0                                 # IF NULL == lookup_type(global_token->S, prim_types)
    jne %statement_local_success                # Sweet a new local

    # Second chance
    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &struct                            # "struct"
    call %match                                 # IF global_token->S == "struct"
    cmp_eax, !0                                 # then we are a local
    jne %statement_if                           # otherwise try IF

:statement_local_success
    call %collect_local                         # Grab those locals
    jmp %statement_done                         # be done

:statement_if
    mov_eax, &if_string                         # Using "if"
    call %match                                 # IF global_token->S == "if"
    cmp_eax, !0                                 # then we have an if statement
    jne %statement_do                           # otherwise try DO

    # Deal with IF statement
    call %process_if                            # DO IT
    jmp %statement_done                         # be done

:statement_do
    mov_eax, &do_string                         # Using "do"
    call %match                                 # IF global_token->S == "do"
    cmp_eax, !0                                 # then we have a do statement
    jne %statement_while                        # otherwise try WHILE

    # Deal with DO statement
    call %process_do                            # DO IT
    jmp %statement_done                         # be done

:statement_while
    mov_eax, &while_string                      # Using "while"
    call %match                                 # IF global_token->S == "while"
    cmp_eax, !0                                 # then we have a while statement
    jne %statement_for                          # otherwise try FOR

    # Deal with WHILE statement
    call %process_while                         # DO IT
    jmp %statement_done                         # be done

:statement_for
    mov_eax, &for_string                        # Using "for"
    call %match                                 # IF global_token->S == "for"
    cmp_eax, !0                                 # then we have a for statement
    jne %statement_asm                          # otherwise try ASM

    # Deal with FOR statement
    call %process_for                           # DO IT
    jmp %statement_done                         # be done

:statement_asm
    mov_eax, &asm_string                        # Using "asm"
    call %match                                 # IF global_token->S == "asm"
    cmp_eax, !0                                 # then we have an asm statement
    jne %statement_goto                         # otherwise try GOTO

    # Deal with ASM statement
    call %process_asm                           # Hit it
    jmp %statement_done                         # be done

:statement_goto
    mov_eax, &goto_string                       # Using "goto"
    call %match                                 # IF global_token->S == "goto"
    cmp_eax, !0                                 # then we have a goto statement
    jne %statement_return                       # Otherwise try RETURN

    # Deal with GOTO statement
    mov_eax, &statement_string_1                # Using "jmp %"
    call %emit_out                              # emit it

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->next
    mov_[DWORD],eax &global_token               # global_token = global_token->next

    mov_eax,[eax+BYTE] !8                       # global_token->S
    call %emit_out                              # emit it

    mov_eax, &statement_string_2                # Using "\n"
    call %emit_out                              # emit it

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->next
    mov_[DWORD],eax &global_token               # global_token = global_token->next

    mov_eax, &statement_string_4                # Using "ERROR in statement\nMissing ;\n"
    mov_ebx, &semicolon                         # Using ";"
    call %require_match                         # Make sure it has the required
    jmp %statement_done                         # Be done

:statement_return
    mov_eax, &return_string                     # Using "return"
    call %match                                 # IF global_token->S == "return"
    cmp_eax, !0                                 # then we have a return statement
    jne %statement_break                        # Otherwise try BREAK

    # Deal with RETURN Statement
    call %return_result                         # Return anything they want
    jmp %statement_done                         # be done

:statement_break
    mov_eax, &break_string                      # Using "break"
    call %match                                 # IF global_token->S == "break"
    cmp_eax, !0                                 # then we have a break statement
    jne %statement_continue                     # Otherwise try CONTINUE

    # Deal with BREAK statement
    call %process_break                         # Lets do some damage
    jmp %statement_done                         # be done

:statement_continue
    mov_eax, &continue_string                   # Using "continue"
    call %match                                 # IF global_token->S == "continue"
    cmp_eax, !0                                 # then we have a continue statement
    jne %statement_else                         # Otherwise we are punting to an expression

    # Deal with CONTINUE statement
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->next
    mov_[DWORD],eax &global_token               # global_token = global_token->next

    mov_eax, &statement_string_3                # Using "\n#continue statement\n"
    call %emit_out                              # emit it

    mov_eax, &statement_string_4                # Using "ERROR in statement\nMissing ;\n"
    mov_ebx, &semicolon                         # Using ";"
    call %require_match                         # Don't forget the ";"
    jmp %statement_done                         # Be done

:statement_else
    call %expression                            # Collect expression
    mov_eax, &statement_string_4                # Using "ERROR in statement\nMissing ;\n"
    mov_ebx, &semicolon                         # Using ";"
    call %require_match                         # make sure we have it

:statement_done
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:statement_string_0  "  #C goto label
"
:statement_string_1  "jmp %"
:statement_string_2  "
"
:statement_string_3  "
#continue statement
"
:statement_string_4  "ERROR in statement
Missing ;
"


# recursive_statement function
# Receives nothing
# Returns nothing
# Walks the global_token list to build the contents of statements
# Uses struct token_list* frame in ECX
:recursive_statement
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->next
    mov_[DWORD],eax &global_token               # global_token = global_token->next

    mov_ecx,[DWORD] &function                   # Using function
    mov_ecx,[ecx+BYTE] !4                       # frame = function->locals

:recursive_statement_loop
    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &close_curly_brace                 # Using "}"
    call %match                                 # IF global_token->S == "}"
    cmp_eax, !0                                 # Then we are done recursing
    je %recursive_statement_cleanup             # and then we clean up

    # Deal with the recursive calls
    call %statement                             # Deal with another statement
    jmp %recursive_statement_loop               # loop some more

:recursive_statement_cleanup
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->next
    mov_[DWORD],eax &global_token               # global_token = global_token->next

    mov_eax, &recursive_statement_string_0      # Using "ret\n"
    mov_ebx,[DWORD] &output_list                # Using output
    mov_ebx,[ebx+BYTE] !8                       # output->S
    call %match                                 # IF output->S == "ret\n"
    cmp_eax, !0                                 # Then we can skip the clean up
    je %recursive_statement_done                # and be done

    # Deal with cleanup
    mov_ebx,[DWORD] &function                   # Using function
    mov_ebx,[ebx+BYTE] !4                       # i = function->locals
    mov_eax, &recursive_statement_string_1      # Using "pop_ebx\t# _recursive_statement_locals\n"

:recursive_statement_locals
    cmp_ebx,ecx                                 # IF frame != i
    je %recursive_statement_done                # Otherwise be done

    # Lets emit
    call %emit_out                              # emit it
    mov_ebx,[ebx]                               # i = i->next
    jmp %recursive_statement_locals             # keep going

:recursive_statement_done
    mov_eax,[DWORD] &function                   # Using function
    mov_[eax+BYTE],ecx !4                       # function->locals = frame
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:recursive_statement_string_0  "ret
"
:recursive_statement_string_1  "pop_ebx # _recursive_statement_locals
"


# return_result function
# Receives nothing
# Returns nothing
# Cleans up function and generates return
# Also handles returning expressions
:return_result
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->next
    mov_[DWORD],eax &global_token               # global_token = global_token->next

    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # make it useful
    cmp_eax, !59                                # If global_token->S[0] == ';'
    je %return_result_cleanup                   # Go straight to cleanup

    call %expression                            # get the expression we are returning

:return_result_cleanup
    mov_eax, &return_result_string_0            # Using "ERROR in return_result\nMISSING ;\n"
    mov_ebx, &semicolon                         # Using ";"
    call %require_match                         # Make sure we have it

    mov_ebx,[DWORD] &function                   # Using function
    mov_ebx,[ebx+BYTE] !4                       # function->locals
    mov_eax, &return_result_string_1            # Using "pop_ebx\t# _return_result_locals\n"
:return_result_locals
    cmp_ebx, !0                                 # IF NULL == i
    je %return_result_done                      # Be done

    call %emit_out                              # Emit out pop
    mov_ebx,[ebx]                               # i = i->NEXT
    jmp %return_result_locals                   # Keep going

:return_result_done
    mov_eax, &return_result_string_2            # Using "ret\n"
    call %emit_out                              # Emit it
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:return_result_string_0  "ERROR in return_result
MISSING ;
"
:return_result_string_1  "pop_ebx   # _return_result_locals
"
:return_result_string_2  "ret
"


# collect_local function
# Receives nothing
# Returns nothing
# Walks global_token list to create function locals
# Uses ECX for struct token_list* A
:collect_local
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    call %type_name                             # Get the local's type

    mov_ebx,eax                                 # Put struct type* type_size in the right place
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_ecx,[DWORD] &function                   # Using function
    mov_ecx,[ecx+BYTE] !4                       # function->locals
    call %sym_declare                           # Declare it
    mov_ecx,eax                                 # put it away safely

    # Try for main
    mov_eax, &main_string                       # Using "main"
    mov_ebx,[DWORD] &function                   # Using function
    mov_ebx,[ebx+BYTE] !8                       # function->S
    call %match                                 # IF match("main", function->s)
    cmp_eax, !0                                 # possible
    jne %collect_local_fresh                    # try to see if fresh function

    # Ok we are in main, now to see if main is fresh
    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !4                       # function->locals
    cmp_eax, !0                                 # IF NULL == function->locals
    jne %collect_local_fresh                    # try to see if fresh function

    # Sweet we are in a fresh main
    mov_eax, %-20                               # We start at -20
    mov_[ecx+BYTE],eax !16                      # a->DEPTH = -20
    jmp %collect_local_common                   # Go to the commons

:collect_local_fresh
    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !16                      # function->args
    cmp_eax, !0                                 # IF NULL == function->args
    jne %collect_local_first                    # Otherwise see if first

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !4                       # function->locals
    cmp_eax, !0                                 # IF NULL == function->locals
    jne %collect_local_first                    # Otherwise try first

    # Sweet we are in a fresh function
    mov_eax, %-8                                # We start at -8
    mov_[ecx+BYTE],eax !16                      # a->DEPTH = -8
    jmp %collect_local_common                   # Go to the commons

:collect_local_first
    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !4                       # function->locals
    cmp_eax, !0                                 # IF NULL == function->locals
    jne %collect_local_else                     # Looks like we are just another local

    # Ok we are the first local
    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !16                      # function->args
    mov_eax,[eax+BYTE] !16                      # function->args->depth
    sub_eax, !8                                 # function->arguments->depth - 8
    mov_[ecx+BYTE],eax !16                      # a->DEPTH = function->arguments->depth - 8
    jmp %collect_local_common                   # Go to the commons

:collect_local_else
    # Always the last to know
    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !4                       # function->locals
    mov_eax,[eax+BYTE] !16                      # function->locals->depth
    sub_eax, !4                                 # function->locals->depth - 4
    mov_[ecx+BYTE],eax !16                      # a->DEPTH = function->locals->depth - 4

:collect_local_common
    mov_eax,[DWORD] &function                   # Using function

    mov_[eax+BYTE],ecx !4                       # function->locals = a
    mov_ecx,[ecx+BYTE] !8                       # a->S

    mov_eax, &collect_local_string_0            # Using "# Defining local "
    call %emit_out                              # emit it

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    call %emit_out                              # emit it

    mov_eax, &collect_local_string_1            # Using "\n"
    call %emit_out                              # emit it

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_ebx,[eax+BYTE] !8                       # global_token->S
    mov_eax, &equal                             # Using "="
    call %match                                 # IF match("=", global_token->s)
    cmp_eax, !0                                 # Deal with assignment
    jne %collect_local_done                     # Otherwise finish it

    # Deal with assignment
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    call %expression                            # Recurse

:collect_local_done
    mov_eax, &collect_local_string_2            # Using "ERROR in collect_local\nMissing ;\n"
    mov_ebx, &semicolon                         # Using ";"
    call %require_match                         # Make sure we have it

    mov_eax, &collect_local_string_3            # Using "push_eax\t#"
    call %emit_out                              # emit it

    mov_eax,ecx                                 # put A->S where it belongs
    call %emit_out                              # emit it

    mov_eax, &collect_local_string_1            # Using "\n"
    call %emit_out                              # emit it

    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:collect_local_string_0  "# Defining local "
:collect_local_string_1  "
"
:collect_local_string_2  "ERROR in collect_local
Missing ;
"
:collect_local_string_3  "push_eax  #"


# process_asm function
# Receives nothing
# Returns nothing
# Simply inlines the asm statements
# Uses EBX for global_token temp storage
:process_asm
    push_ebx                                    # Protect EBX
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax, &process_asm_string_0              # Using "ERROR in process_asm\nMISSING (\n"
    mov_ebx, &open_paren                        # Using "("
    call %require_match                         # Make sure we have it

    mov_ebx,[DWORD] &global_token               # Using global_token
:process_asm_iter
    mov_eax,[ebx+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # Make it useful
    cmp_eax, !34                                # IF global_token->S[0] == '\"'
    jne %process_asm_done                       # Otherwise be done

    mov_eax,[ebx+BYTE] !8                       # global_token->S
    add_eax, !1                                 # global_token->S + 1
    call %emit_out                              # Emit it

    mov_eax, &process_asm_string_1              # Using "\n"
    call %emit_out                              # Emit it

    mov_ebx,[ebx]                               # global_token->NEXT
    mov_[DWORD],ebx &global_token               # global_token = global_token->NEXT
    jmp %process_asm_iter                       # keep going

:process_asm_done
    mov_eax, &process_asm_string_2              # Using "ERROR in process_asm\nMISSING )\n"
    mov_ebx, &close_paren                       # Using ")"
    call %require_match                         # Make sure we have it

    mov_eax, &process_asm_string_3              # Using "ERROR in process_asm\nMISSING ;\n"
    mov_ebx, &semicolon                         # Using ";"
    call %require_match                         # Make sure we have it

    pop_ebx                                     # Restore EBX
    ret

:process_asm_string_0  "ERROR in process_asm
MISSING (
"
:process_asm_string_1  "
"
:process_asm_string_2  "ERROR in process_asm
MISSING )
"
:process_asm_string_3  "ERROR in process_asm
MISSING ;
"


# process_if function
# Receives nothing
# Returns Nothing
# Increments current_count recurses into expression + statement
# Uses ECX for char* NUMBER_STRING
:process_if
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_eax,[DWORD] &current_count              # Using current count
    mov_ebx,eax                                 # Preparing for update
    add_ebx, !1                                 # current_count + 1
    mov_[DWORD],ebx &current_count              # current_count = current_count + 1
    call %numerate_number                       # convert to string
    mov_ecx,eax                                 # put NUMBER_STRING in place

    mov_eax, &process_if_string_0               # Using "# IF_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax, &process_if_string_1               # Using "ERROR in process_if\nMISSING (\n"
    mov_ebx, &open_paren                        # Using "("
    call %require_match                         # Make sure we have it

    call %expression                            # Recurse to get the IF(...) part

    mov_eax, &process_if_string_2               # Using "test_eax,eax\nje %ELSE_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax, &process_if_string_3               # Using "ERROR in process_if\nMISSING )\n"
    mov_ebx, &close_paren                       # Using ")"
    call %require_match                         # Make sure we have it

    call %statement                             # Recursive to get the IF(){...} part

    mov_eax, &process_if_string_4               # Using "jmp %_END_IF_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax, &process_if_string_5               # Using ":ELSE_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &else_string                       # Using "else"
    call %match                                 # IF global_token->S == "else"
    cmp_eax, !0                                 # Then we need to collect the else too
    jne %process_if_done                        # Otherwise finish up

    # deal with else statement
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    call %statement                             # Recurse to get the ELSE {...} part

:process_if_done
    mov_eax, &process_if_string_6               # Using ":_END_IF_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:process_if_string_0  "# IF_"
:process_if_string_1  "ERROR in process_if
MISSING (
"
:process_if_string_2  "test_eax,eax
je %ELSE_"
:process_if_string_3  "ERROR in process_if
MISSING )
"
:process_if_string_4  "jmp %_END_IF_"
:process_if_string_5  ":ELSE_"
:process_if_string_6  ":_END_IF_"


# save_break_frame microfunction
# Overwrites EAX and EBX
# Saves break frame on stack
# Returns to caller
:save_break_frame
    pop_ebx                                     # Save return Address
    mov_eax,[DWORD] &break_frame                # Get break_frame
    push_eax                                    # Store as nested_locals
    mov_eax,[DWORD] &break_target_head          # Get break_target_head
    push_eax                                    # Store as nested_break_head
    mov_eax,[DWORD] &break_target_func          # Get break_target_func
    push_eax                                    # Store as nested_break_func
    mov_eax,[DWORD] &break_target_num           # Get break_target_num
    push_eax                                    # Store as nested_break_num
    push_ebx                                    # Put return back in place
    ret                                         # Return to caller


# restore_break_frame microfunction
# Overwrites EAX and EBX
# Restores break frame from stack
# Returns to caller
:restore_break_frame
    pop_ebx                                     # Save return Address
    pop_eax                                     # Get nested_break_num
    mov_[DWORD],eax &break_target_num           # Restore break_target_num
    pop_eax                                     # Get nested_break_func
    mov_[DWORD],eax &break_target_func          # Restore break_target_func
    pop_eax                                     # Get nested_break_head
    mov_[DWORD],eax &break_target_head          # Restore break_target_head
    pop_eax                                     # Get nested_locals
    mov_[DWORD],eax &break_frame                # Restore break_frame
    push_ebx                                    # Put return back in place
    ret                                         # Return to caller


# set_break_frame microfunction
# Receives char* head in EAX and char* num in EBX
# Overwrites EAX and EBX
# Returns to calling function
:set_break_frame
    mov_[DWORD],eax &break_target_head          # update break_target_head
    mov_[DWORD],ebx &break_target_num           # update break_target_num
    mov_ebx,[DWORD] &function                   # Using function
    mov_eax,[ebx+BYTE] !4                       # function->LOCALS
    mov_[DWORD],eax &break_frame                # break_frame = function->LOCALS
    mov_eax,[ebx+BYTE] !8                       # function->S
    mov_[DWORD],eax &break_target_func          # break_target_func = function->S
    ret                                         # Return to sender


# process_do function
# Receives Nothing
# Returns Nothing
# Increments current_count and leverages save/restore_break_frame pieces
# Uses ECX for char* NUMBER_STRING
:process_do
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    call %save_break_frame                      # Save the frame

    mov_eax,[DWORD] &current_count              # Using current count
    mov_ebx,eax                                 # Preparing for update
    add_ebx, !1                                 # current_count + 1
    mov_[DWORD],ebx &current_count              # current_count = current_count + 1
    call %numerate_number                       # convert to string
    mov_ecx,eax                                 # put NUMBER_STRING in place

    mov_eax, &process_do_string_0               # Using "DO_END_"
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %set_break_frame                       # Set the frame

    mov_eax, &process_do_string_1               # Using ":DO_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    call %statement                             # Do the DO {...} part

    mov_eax, &process_do_string_2               # Using "ERROR in process_do\nMISSING while\n"
    mov_ebx, &while_string                      # Using "while"
    call %require_match                         # Make sure we have it

    mov_eax, &process_do_string_3               # Using "ERROR in process_do\nMISSING (\n"
    mov_ebx, &open_paren                        # Using "("
    call %require_match                         # Make sure we have it

    call %expression                            # Do the WHILE (...) part

    mov_eax, &process_do_string_4               # Using "ERROR in process_do\nMISSING )\n"
    mov_ebx, &close_paren                       # Using ")"
    call %require_match                         # Make sure we have it

    mov_eax, &process_do_string_5               # Using "ERROR in process_do\nMISSING ;\n"
    mov_ebx, &semicolon                         # Using ";"
    call %require_match                         # Make sure we have it

    mov_eax, &process_do_string_6               # Using "test_eax,eax\njne %DO_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax, &process_do_string_7               # Using ":DO_END_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    call %restore_break_frame                   # Restore the old break frame

    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:process_do_string_0  "DO_END_"
:process_do_string_1  ":DO_"
:process_do_string_2  "ERROR in process_do
MISSING while
"
:process_do_string_3  "ERROR in process_do
MISSING (
"
:process_do_string_4  "ERROR in process_do
MISSING )
"
:process_do_string_5  "ERROR in process_do
MISSING ;
"
:process_do_string_6  "test_eax,eax
jne %DO_"
:process_do_string_7  ":DO_END_"


# process_while function
# Receives nothing
# Returns nothing
# Increments current_count and leverages save/restore_break_frame pieces
# Uses ECX for char* NUMBER_STRING
:process_while
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    call %save_break_frame                      # Save break_frame

    mov_eax,[DWORD] &current_count              # Using current count
    mov_ebx,eax                                 # Preparing for update
    add_ebx, !1                                 # current_count + 1
    mov_[DWORD],ebx &current_count              # current_count = current_count + 1
    call %numerate_number                       # convert to string
    mov_ecx,eax                                 # put NUMBER_STRING in place

    mov_eax, &process_while_string_0            # Using "END_WHILE_"
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %set_break_frame                       # Set it and forget it

    mov_eax, &process_while_string_1            # Using ":WHILE_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax, &process_while_string_2            # Using "ERROR in process_while\nMISSING (\n"
    mov_ebx, &open_paren                        # Using "("
    call %require_match                         # Make sure we have it

    call %expression                            # Deal with the WHILE (...) part

    mov_eax, &process_while_string_3            # Using "test_eax,eax\nje %END_WHILE_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax, &process_while_string_4            # Using "# THEN_while_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax, &process_while_string_5            # Using "ERROR in process_while\nMISSING )\n"
    mov_ebx, &close_paren                       # Using ")"
    call %require_match                         # Make sure we have it

    call %statement                             # Deal with the {....} part

    mov_eax, &process_while_string_6            # Using "jmp %WHILE_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax, &process_while_string_7            # Using ":END_WHILE_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    call %restore_break_frame                   # Restore the old break frame

    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:process_while_string_0  "END_WHILE_"
:process_while_string_1  ":WHILE_"
:process_while_string_2  "ERROR in process_while
MISSING (
"
:process_while_string_3  "test_eax,eax
je %END_WHILE_"
:process_while_string_4  "# THEN_while_"
:process_while_string_5  "ERROR in process_while
MISSING )
"
:process_while_string_6  "jmp %WHILE_"
:process_while_string_7  ":END_WHILE_"


# process_for function
# Receives Nothing
# Returns Nothing
# Increments current_count and leverages save/restore_break_frame pieces
# Uses ECX for char* NUMBER_STRING
:process_for
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    call %save_break_frame                      # Save the frame

    mov_eax,[DWORD] &current_count              # Using current count
    mov_ebx,eax                                 # Preparing for update
    add_ebx, !1                                 # current_count + 1
    mov_[DWORD],ebx &current_count              # current_count = current_count + 1
    call %numerate_number                       # convert to string
    mov_ecx,eax                                 # put NUMBER_STRING in place

    mov_eax, &process_for_string_0              # Using "FOR_END_"
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %set_break_frame                       # Set it and forget it

    mov_eax, &process_for_string_1              # Using "# FOR_initialization_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax, &process_for_string_2              # Using "ERROR in process_for\nMISSING (\n"
    mov_ebx, &open_paren                        # Using "("
    call %require_match                         # Make Sure we have it

    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &semicolon                         # Using ";"
    call %match                                 # IF global_token->S == ";"
    cmp_eax, !0                                 # Then no initializer
    je %process_for_terminator                  # And skip getting the expression

    # Deal with FOR (...; case
    call %expression                            # Get the FOR ( ... ; part

:process_for_terminator
    mov_eax, &process_for_string_3              # Using ":FOR_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax, &process_for_string_4              # Using "ERROR in process_for\nMISSING ;1\n"
    mov_ebx, &semicolon                         # Using ";"
    call %require_match                         # Make sure we have it

    call %expression                            # Get the FOR ( ; ... ; Part

    mov_eax, &process_for_string_5              # Using "test_eax,eax\nje %FOR_END_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax, &process_for_string_6              # Using "jmp %FOR_THEN_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax, &process_for_string_7              # Using ":FOR_ITER_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax, &process_for_string_8              # Using "ERROR in process_for\nMISSING ;2\n"
    mov_ebx, &semicolon                         # Using ";"
    call %require_match                         # Make sure we have it

    call %expression                            # Get the FOR (;;...) part

    mov_eax, &process_for_string_9              # Using "jmp %FOR_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax, &process_for_string_10             # Using ":FOR_THEN_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax, &process_for_string_11             # Using "ERROR in process_for\nMISSING )\n"
    mov_ebx, &close_paren                       # Using ")"
    call %require_match                         # Make sure we have it

    call %statement                             # Get FOR (;;) {...} part

    mov_eax, &process_for_string_12             # Using "jmp %FOR_ITER_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Passing NUMBER_STRING
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    mov_eax, &process_for_string_13             # Using ":FOR_END_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    call %uniqueID_out                          # uniqueID_out(function->s, number_string)

    call %restore_break_frame                   # Restore the old break frame

    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:process_for_string_0  "FOR_END_"
:process_for_string_1  "# FOR_initialization_"
:process_for_string_2  "ERROR in process_for
MISSING (
"
:process_for_string_3  ":FOR_"
:process_for_string_4  "ERROR in process_for
MISSING ;1
"
:process_for_string_5  "test_eax,eax
je %FOR_END_"
:process_for_string_6  "jmp %FOR_THEN_"
:process_for_string_7  ":FOR_ITER_"
:process_for_string_8  "ERROR in process_for
MISSING ;2
"
:process_for_string_9  "jmp %FOR_"
:process_for_string_10 ":FOR_THEN_"
:process_for_string_11 "ERROR in process_for
MISSING )
"
:process_for_string_12 "jmp %FOR_ITER_"
:process_for_string_13 ":FOR_END_"


# process_break function
# Receives nothing
# Returns nothing
# Handles the break out of loops case
# Uses EBX for struct token_list* break_frame and ECX for struct token_list* I
:process_break
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_eax,[DWORD] &break_target_head          # Catch big error
    cmp_eax, !0                                 # IF(NULL == break_target_head)
    je %process_break_bad                       # I'm sorry Mr White but you have stage-3 lung cancer

    mov_eax,[DWORD] &function                   # Using function
    mov_ecx,[eax+BYTE] !4                       # I = function->LOCALS
    mov_ebx,[DWORD] &break_frame                # Put break_frame in the right spot
    mov_eax, &process_break_string_1            # Using "pop_ebx\t# break_cleanup_locals\n"

:process_break_iter
    cmp_ecx, !0                                 # IF (NULL == I)
    je %process_break_cleaned                   # We are done

    cmp_ebx,ecx                                 # IF I != break_frame
    je %process_break_cleaned                   # We are done

    call %emit_out                              # Emit it
    mov_ecx,[ecx]                               # I = I->NEXT
    jmp %process_break_iter                     # Keep looping

:process_break_cleaned
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax, &process_break_string_2            # Using "jmp %"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &break_target_head          # Get what we are in
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &break_target_func          # Get what function we are in
    call %emit_out                              # Emit it

    mov_eax, &underline                         # Using "_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &break_target_num           # Get dem digits
    call %emit_out                              # Emit it

    mov_eax, &process_break_string_3            # Using "\n"
    call %emit_out                              # Emit it

    mov_eax, &process_break_string_4            # Using "ERROR in break statement\nMissing ;\n"
    mov_ebx, &semicolon                         # Using ";"
    call %require_match                         # Make sure we have it

    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:process_break_bad
    # Breaking badly
    mov_eax, %2                                 # Using standard error
    mov_[DWORD],eax &Output_file                # write to standard error
#   call %line_error                            # Write useful debug info
    mov_eax,ecx                                 # put S in the right place
    call %File_Print                            # print it

    mov_eax, &process_break_string_0            # Ending string
    call %File_Print                            # print it
    jmp %Exit_Failure                           # Abort Hard

:process_break_string_0  "Not inside of a loop or case statement"
:process_break_string_1  "pop_ebx   # break_cleanup_locals
"
:process_break_string_2  "jmp %"
:process_break_string_3  "
"
:process_break_string_4  "ERROR in break statement
Missing ;
"


# expression function
# Receives Nothing
# Returns Nothing
# Walks global_token and updates output_list
# Uses EAX and EBX for match and ECX for char* store
:expression
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    call %bitwise_expr                          # Collect bitwise expressions

    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &equal                             # "="
    call %match                                 # IF global_token->S == "="
    cmp_eax, !0                                 # We have to deal with assignment
    jne %expression_done                        # Looks like nope

    # Deal with possible assignment
    mov_ecx, &expression_string_1               # Assume "mov_[ebx],al\n" by default
    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !4                       # global_token->PREV
    mov_ebx,[ebx+BYTE] !8                       # global_token->PREV->S
    mov_eax, &close_bracket                     # Using "]"
    call %match                                 # IF global_token->S == "]"
    cmp_eax, !0                                 # Then we might have a char
    jne %expression_int                         # Otherwise INT

    mov_ebx,[DWORD] &current_target             # Using current_target
    mov_ebx,[ebx+BYTE] !24                      # current_target->NAME
    mov_eax, &type_char_indirect_name           # Using "char*"
    call %match                                 # Intentional inefficiency because I feel like it
    cmp_eax, !0                                 # IF current_target->NAME == "char*"
    jne %expression_int                         # Do char anyway

    jmp %expression_common                      # Looks like we have to use "mov_[ebx],al\n"

:expression_int
    mov_ecx, &expression_string_0               # Use "mov_[ebx],eax\n"

:expression_common
    mov_eax, &expression                        # Passing expression
    call %common_recursion                      # Recurse
    mov_eax,ecx                                 # Using Store
    call %emit_out                              # Emit it
    mov_eax, %0                                 # Using NULL
    mov_[DWORD],eax &current_target             # current_target = NULL

:expression_done
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:expression_string_0  "mov_[ebx],eax
"
:expression_string_1  "mov_[ebx],al
"


# bitwise_expr function
# Receives nothing
# Returns nothing
# Walks global_token list and updates output list
# Just calls other functions
:bitwise_expr
    call %relational_expr                       # Walk up the tree
    call %bitwise_expr_stub                     # Let general recursion do the work
    ret


# bitwise_expr_stub function
# Receives nothing
# Returns Nothing
# Just calls general_recursion a bunch
# Uses EAX, EBX, ECX and EDX for passing constants to general recursion
:bitwise_expr_stub
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX

    mov_eax, &relational_expr                   # Using relational_expr
    mov_ebx, &bitwise_expr_stub_string_0        # Using "and_eax,ebx\n"
    mov_ecx, &bitwise_and                       # Using "&"
    mov_edx, &bitwise_expr_stub                 # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &relational_expr                   # Using relational_expr
    mov_ebx, &bitwise_expr_stub_string_0        # Using "and_eax,ebx\n"
    mov_ecx, &logical_and                       # Using "&&"
    mov_edx, &bitwise_expr_stub                 # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &relational_expr                   # Using relational_expr
    mov_ebx, &bitwise_expr_stub_string_1        # Using "or_eax,ebx\n"
    mov_ecx, &bitwise_or                        # Using "|"
    mov_edx, &bitwise_expr_stub                 # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &relational_expr                   # Using relational_expr
    mov_ebx, &bitwise_expr_stub_string_1        # Using "or_eax,ebx\n"
    mov_ecx, &logical_or                        # Using "||"
    mov_edx, &bitwise_expr_stub                 # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &relational_expr                   # Using relational_expr
    mov_ebx, &bitwise_expr_stub_string_2        # Using "xor_eax,ebx\n"
    mov_ecx, &bitwise_xor                       # Using "^"
    mov_edx, &bitwise_expr_stub                 # And recurse
    call %general_recursion                     # Hit it

    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:bitwise_expr_stub_string_0  "and_eax,ebx
"
:bitwise_expr_stub_string_1  "or_eax,ebx
"
:bitwise_expr_stub_string_2  "xor_eax,ebx
"


# relational_expr function
# Receives nothing
# Returns Nothing
# Walks global_token list and updates output list
# just calls other function
:relational_expr
    call %additive_expr                         # Walk up the tree
    call %relational_expr_stub                  # Recurse
    ret


# relational_expr_stub function
# Receives nothing
# Returns Nothing
# Just calls general_recursion a bunch
# Uses EAX, EBX, ECX and EDX for passing constants to general recursion
:relational_expr_stub
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX

    mov_eax, &additive_expr                     # Using additive_expr
    mov_ebx, &relational_expr_stub_string_0     # Using "cmp\nsetl_al\nmovzx_eax,al\n"
    mov_ecx, &less_than_string                  # Using "<"
    mov_edx, &relational_expr_stub              # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &additive_expr                     # Using additive_expr
    mov_ebx, &relational_expr_stub_string_1     # Using "cmp\nsetle_al\nmovzx_eax,al\n"
    mov_ecx, &less_than_equal_string            # Using "<="
    mov_edx, &relational_expr_stub              # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &additive_expr                     # Using additive_expr
    mov_ebx, &relational_expr_stub_string_2     # Using "cmp\nsetge_al\nmovzx_eax,al\n"
    mov_ecx, &greater_than_equal_string         # Using ">="
    mov_edx, &relational_expr_stub              # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &additive_expr                     # Using additive_expr
    mov_ebx, &relational_expr_stub_string_3     # Using "cmp\nsetg_al\nmovzx_eax,al\n"
    mov_ecx, &greater_than_string               # Using ">"
    mov_edx, &relational_expr_stub              # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &additive_expr                     # Using additive_expr
    mov_ebx, &relational_expr_stub_string_4     # Using "cmp\nsete_al\nmovzx_eax,al\n"
    mov_ecx, &equal_to_string                   # Using "=="
    mov_edx, &relational_expr_stub              # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &additive_expr                     # Using additive_expr
    mov_ebx, &relational_expr_stub_string_5     # Using "cmp\nsetne_al\nmovzx_eax,al\n"
    mov_ecx, &not_equal_string                  # Using "!="
    mov_edx, &relational_expr_stub              # And recurse
    call %general_recursion                     # Hit it

    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:relational_expr_stub_string_0  "cmp
setl_al
movzx_eax,al
"
:relational_expr_stub_string_1  "cmp
setle_al
movzx_eax,al
"
:relational_expr_stub_string_2  "cmp
setge_al
movzx_eax,al
"
:relational_expr_stub_string_3  "cmp
setg_al
movzx_eax,al
"
:relational_expr_stub_string_4  "cmp
sete_al
movzx_eax,al
"
:relational_expr_stub_string_5  "cmp
setne_al
movzx_eax,al
"


# additive_expr function
# Receives nothing
# Returns Nothing
# Walks global_token list and updates output list
# just calls other function
:additive_expr
    call %postfix_expr                          # Walk up the tree
    call %additive_expr_stub                    # Recurse
    ret


# additive_expr_stub function
# Receives nothing
# Returns Nothing
# Just calls general_recursion a bunch
# Uses EAX, EBX, ECX and EDX for passing constants to general recursion
:additive_expr_stub
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX

    mov_eax, &postfix_expr                      # Using postfix_expr
    mov_ebx, &additive_expr_stub_string_0       # Using "add_eax,ebx\n"
    mov_ecx, &plus_string                       # Using "+"
    mov_edx, &additive_expr_stub                # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &postfix_expr                      # Using postfix_expr
    mov_ebx, &additive_expr_stub_string_1       # Using "sub_ebx,eax\nmov_eax,ebx\n"
    mov_ecx, &minus_string                      # Using "-"
    mov_edx, &additive_expr_stub                # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &postfix_expr                      # Using postfix_expr
    mov_ebx, &additive_expr_stub_string_2       # Using "mul_ebx\n"
    mov_ecx, &multiply_string                   # Using "*"
    mov_edx, &additive_expr_stub                # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &postfix_expr                      # Using postfix_expr
    mov_ebx, &additive_expr_stub_string_3       # Using "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\n"
    mov_ecx, &divide_string                     # Using "/"
    mov_edx, &additive_expr_stub                # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &postfix_expr                      # Using postfix_expr
    mov_ebx, &additive_expr_stub_string_4       # Using "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\nmov_eax,edx\n"
    mov_ecx, &modulus_string                    # Using "%"
    mov_edx, &additive_expr_stub                # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &postfix_expr                      # Using postfix_expr
    mov_ebx, &additive_expr_stub_string_5       # Using "mov_ecx,eax\nmov_eax,ebx\nsal_eax,cl\n"
    mov_ecx, &left_shift_string                 # Using "<<"
    mov_edx, &additive_expr_stub                # And recurse
    call %general_recursion                     # Hit it

    mov_eax, &postfix_expr                      # Using postfix_expr
    mov_ebx, &additive_expr_stub_string_6       # Using "mov_ecx,eax\nmov_eax,ebx\nsar_eax,cl\n"
    mov_ecx, &right_shift_string                # Using ">>"
    mov_edx, &additive_expr_stub                # And recurse
    call %general_recursion                     # Hit it

    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:additive_expr_stub_string_0  "add_eax,ebx
"
:additive_expr_stub_string_1  "sub_ebx,eax
mov_eax,ebx
"
:additive_expr_stub_string_2  "mul_ebx
"
:additive_expr_stub_string_3  "xchg_ebx,eax
mov_edx, %0
div_ebx
"
:additive_expr_stub_string_4  "xchg_ebx,eax
mov_edx, %0
div_ebx
mov_eax,edx
"
:additive_expr_stub_string_5  "mov_ecx,eax
mov_eax,ebx
sal_eax,cl
"
:additive_expr_stub_string_6  "mov_ecx,eax
mov_eax,ebx
sar_eax,cl
"


# postfix_expr function
# Receives nothing
# Returns Nothing
# Walks global_token list and updates output list
# just calls other function
:postfix_expr
    call %primary_expr                          # Walk up the tree
    call %postfix_expr_stub                     # Recurse
    ret


# postfix_expr_stub function
# Receives nothing
# Returns Nothing
# Checks for "[" and "->" and deals with them otherwise does nothing
# Uses EAX, EBX, ECX and EDX for passing constants to general recursion
:postfix_expr_stub
    push_ebx                                    # Protect EBX
    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &open_bracket                      # Using "["
    call %match                                 # IF global_token->S == "["
    cmp_eax, !0                                 # then we have an array
    jne %postfix_expr_stub_arrow                # Otherwise try arrow

    # Deal with array
    call %postfix_expr_array                    # Get it
    call %postfix_expr_stub                     # Recurse

:postfix_expr_stub_arrow
    mov_eax, &arrow_string                      # Using "->"
    call %match                                 # IF global_token->S == "->"
    cmp_eax, !0                                 # Then we need to deal with struct offsets
    jne %postfix_expr_stub_done                 # Otherwise be done

    # Deal with arrow
    call %postfix_expr_arrow                    # Get it
    call %postfix_expr_stub                     # Recurse

:postfix_expr_stub_done
    pop_ebx                                     # Restore EBX
    ret


# unary_expr_sizeof function
# Receives nothing
# Returns nothing
# Uses ECX for A->SIZE
:unary_expr_sizeof
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax, &unary_expr_sizeof_string_0        # Using "ERROR in unary_expr\nMissing (\n"
    mov_ebx, &open_paren                        # Using "("
    call %require_match                         # Make sure we have it

    call %type_name                             # Get the type
    mov_ecx,[eax+BYTE] !4                       # Set A->TYPE

    mov_eax, &unary_expr_sizeof_string_1        # Using "ERROR in unary_expr\nMissing )\n"
    mov_ebx, &close_paren                       # Using ")"
    call %require_match                         # Make sure we have it

    mov_eax, &unary_expr_sizeof_string_2        # Using "mov_eax, %"
    call %emit_out                              # Emit it

    mov_eax,ecx                                 # Put A->SIZE in the right place
    call %numerate_number                       # Turn into string
    call %emit_out                              # Emit it

    mov_eax, &unary_expr_sizeof_string_3        # Using "\n"
    call %emit_out                              # Emit it

    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:unary_expr_sizeof_string_0  "ERROR in unary_expr
Missing (
"
:unary_expr_sizeof_string_1  "ERROR in unary_expr
Missing )
"
:unary_expr_sizeof_string_2  "mov_eax, %"
:unary_expr_sizeof_string_3  "
"


# postfix_expr_array function
# Receives Nothing
# Returns Nothing
# Uses EBX for struct type* ARRAY and ECX for char* ASSIGN
:postfix_expr_array
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_eax,[DWORD] &current_target             # ARRAY = current_target
    push_eax                                    # Protect it

    mov_eax, &expression                        # Using expression
    call %common_recursion                      # Recurse

    pop_ebx                                     # Restore array
    mov_[DWORD],ebx &current_target             # current_target = ARRAY

    mov_ecx, &postfix_expr_array_string_0       # ASSIGN = "mov_eax,[eax]\n"

    mov_eax, &type_char_indirect_name           # Using "char*"
    mov_ebx,[ebx+BYTE] !24                      # current_target->NAME
    call %match                                 # IF current_target->NAME == "char*"
    cmp_eax, !0                                 # load a byte
    jne %postfix_expr_array_large               # Otherwise adjust

    # Deal with loading byte
    mov_ecx, &postfix_expr_array_string_1       # ASSIGN = "movsx_eax,BYTE_PTR_[eax]\n"
    jmp %postfix_expr_array_common              # Do the next bit

:postfix_expr_array_large
    # deal with arrays made of things other than chars
    mov_eax, &postfix_expr_array_string_2       # Using "sal_eax, !"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &current_target             # Using current_target
    mov_eax,[eax+BYTE] !12                      # current_target->INDIRECT
    mov_eax,[eax+BYTE] !4                       # current_target->INDIRECT->SIZE
    call %ceil_log2                             # ceil_log2(current_target->indirect->size)
    call %numerate_number                       # numerate_number(ceil_log2(current_target->indirect->size))
    call %emit_out                              # Emit it

    mov_eax, &postfix_expr_array_string_3       # Using "\n"
    call %emit_out                              # Emit it

:postfix_expr_array_common
    mov_eax, &postfix_expr_array_string_4       # Using "add_eax,ebx\n"
    call %emit_out                              # Emit it

    mov_eax, &postfix_expr_array_string_5       # Using "ERROR in postfix_expr\nMissing ]\n"
    mov_ebx, &close_bracket                     # Using "]"
    call %require_match                         # Make sure we have it

    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &equal                             # Using "="
    call %match                                 # IF global_token->S == "="
    cmp_eax, !0                                 # We need to preserve address
    jne %postfix_expr_array_done                # Otherwise be done

    # Clearing out assign
    mov_ecx, &postfix_expr_array_string_6       # ASSIGN = ""

:postfix_expr_array_done
    mov_eax,ecx                                 # Using ASSIGN
    call %emit_out                              # Emit it

    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:postfix_expr_array_string_0  "mov_eax,[eax]
"
:postfix_expr_array_string_1  "movsx_eax,BYTE_PTR_[eax]
"
:postfix_expr_array_string_2  "sal_eax, !"
:postfix_expr_array_string_3  "
"
:postfix_expr_array_string_4  "add_eax,ebx
"
:postfix_expr_array_string_5  "ERROR in postfix_expr
Missing ]
"
:postfix_expr_array_string_6  ""


# ceil_log2 function
# Receives int a in EAX
# Performs log2 on A and
# Returns result in EAX
# Uses EBX for INT A and ECX for INT RESULT
:ceil_log2
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ecx, %0                                 # RESULT = 0

    mov_ebx,eax                                 # put A in right place
    sub_eax, !1                                 # (A - 1)
    and_eax,ebx                                 # A & (A - 1)
    cmp_eax, !0                                 # IF 0 == (A & (A - 1))
    jne %ceil_log2_iter                         # Starting from -1

    mov_ecx, %-1                                # RESULT = -1

:ceil_log2_iter
    cmp_ebx, !0                                 # IF A > 0
    jle %ceil_log2_done                         # Otherwise be done

    add_ecx, !1                                 # RESULT = RESULT + 1
    shr_ebx, !1                                 # A = A >> 1
    jmp %ceil_log2_iter                         # Keep looping

:ceil_log2_done
    mov_eax,ecx                                 # Return RESULT
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# postfix_expr_arrow function
# Receives nothing
# Returns nothing
# Emits a bunch and updates current_target
# Uses EBX for struct type* I
:postfix_expr_arrow
    push_ebx                                    # Protect EBX
    mov_eax, &postfix_expr_arrow_string_0       # Using "# looking up offset\n"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_ebx,[eax+BYTE] !8                       # Using global_token->S
    mov_eax,[DWORD] &current_target             # Using current_target
    call %lookup_member                         # lookup_member(current_target, global_token->s)
    mov_ebx,eax                                 # struct type* I = lookup_member(current_target, global_token->s)

    mov_eax,[eax+BYTE] !20                      # I->TYPE
    mov_[DWORD],eax &current_target             # current_target = I->TYPE

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax,[ebx+BYTE] !8                       # I->OFFSET
    cmp_eax, !0                                 # IF 0 != I->OFFSET
    je %postfix_expr_arrow_first                # Then we don't need to do an offset

    # Deal with needing an offset
    mov_eax, &postfix_expr_arrow_string_1       # Using "# -> offset calculation\nmov_ebx, %"
    call %emit_out                              # Emit it

    mov_eax,[ebx+BYTE] !8                       # I->OFFSET
    call %numerate_number                       # Convert to string
    call %emit_out                              # Emit it

    mov_eax, &postfix_expr_arrow_string_2       # Using "\nadd_eax,ebx\n"
    call %emit_out                              # Emit it

:postfix_expr_arrow_first
    mov_eax,[ebx+BYTE] !4                       # I->SIZE
    cmp_eax, !4                                 # IF I->SIZE >= 4
    jl %postfix_expr_arrow_done                 # Otherwise be done

    # Last chance for load
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_ebx,[eax+BYTE] !8                       # global_token->S
    mov_eax, &equal                             # Using "="
    call %match                                 # IF global_token->S == "="
    cmp_eax, !0                                 # Then we have assignment and should not load
    je %postfix_expr_arrow_done                 # Be done

    # Deal with load case
    mov_eax, &postfix_expr_arrow_string_3       # Using "mov_eax,[eax]\n"
    call %emit_out                              # Emit it

:postfix_expr_arrow_done
    pop_ebx                                     # Restore EBX
    ret

:postfix_expr_arrow_string_0  "# looking up offset
"
:postfix_expr_arrow_string_1  "# -> offset calculation
mov_ebx, %"
:postfix_expr_arrow_string_2  "
add_eax,ebx
"
:postfix_expr_arrow_string_3  "mov_eax,[eax]
"


# primary_expr function
# Receives nothing
# Returns nothing
:primary_expr
    push_ebx                                    # Protect EBX

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_ebx,[eax+BYTE] !8                       # global_token->S
    mov_eax, &sizeof_string                     # Using "sizeof"
    call %match                                 # See if match
    cmp_eax, !0                                 # IF match
    jne %primary_expr_neg                       # Otherwise try negatives

    # Deal with sizeof
    call %unary_expr_sizeof                     # Lets do this
    jmp %primary_expr_done                      # Be done

:primary_expr_neg
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # Make it useful
    cmp_eax, !45                                # IF global_token->S[0] == "-"
    jne %primary_expr_not                       # Otherwise try logical NOT

    # Deal with negative numbers
    mov_eax, &primary_expr_string_0             # Using "mov_eax, %0\n"
    call %emit_out                              # Emit it

    mov_eax, &postfix_expr                      # Passing postfix_expr
    call %common_recursion                      # Get what it is notting

    mov_eax, &primary_expr_string_1             # Using "sub_ebx,eax\nmov_eax,ebx\n"
    call %emit_out                              # Emit it
    jmp %primary_expr_done                      # Be done

:primary_expr_not
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # Make it useful
    cmp_eax, !33                                # IF global_token->S[0] == "!"
    jne %primary_expr_bin                       # Otherwise try '~'

    # Deal with logical not
    mov_eax, &primary_expr_string_2             # Using "mov_eax, %1\n"
    call %emit_out                              # Emit it

    mov_eax, &postfix_expr                      # Passing postfix_expr
    call %common_recursion                      # Get what it is notting

    mov_eax, &primary_expr_string_3             # Using "xor_eax,ebx\n"
    call %emit_out                              # Emit it
    jmp %primary_expr_done                      # Be done

:primary_expr_bin
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # Make it useful
    cmp_eax, !126                               # IF global_token->S[0] == "~"
    jne %primary_expr_paren                     # Otherwise try paren

    # Deal with binary NOT
    mov_eax, &postfix_expr                      # Passing postfix_expr
    call %common_recursion                      # Get what it is notting
    mov_eax, &primary_expr_string_4             # Using "not_eax\n"
    call %emit_out                              # Emit it
    jmp %primary_expr_done                      # Be done

:primary_expr_paren
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # Make it useful
    cmp_eax, !40                                # IF global_token->S[0] == "("
    jne %primary_expr_ch                        # Otherwise try char

    # deal with nesting
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT
    call %expression                            # Lets recurse
    mov_eax, &primary_expr_string_5             # Using "Error in Primary expression\nDidn't get )\n"
    mov_ebx, &close_paren                       # Using ")"
    call %require_match                         # Make sure we have it
    jmp %primary_expr_done                      # Be done

:primary_expr_ch
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # Make it useful
    cmp_eax, !39                                # Using "'"
    jne %primary_expr_str                       # Otherwise try string

    # Deal with chars
    call %primary_expr_char                     # Handle that char
    jmp %primary_expr_done                      # Be done

:primary_expr_str
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # Make it useful
    cmp_eax, !34                                # Using '\"'
    jne %primary_expr_var                       # Otherwise try a variable

    # Deal with strings
    call %primary_expr_string                   # Handle that string
    jmp %primary_expr_done                      # Be done

:primary_expr_var
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # Make it useful
    mov_ebx, &primary_expr_string_6             # Using "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
    call %In_Set                                # See if we have a match
    cmp_eax, !1                                 # IF match
    jne %primary_expr_num                       # otherwise try number

    # Deal with variables
    call %primary_expr_variable                 # Deal with variable
    jmp %primary_expr_done                      # Be done

:primary_expr_num
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # Make it useful
    mov_ebx, &primary_expr_string_7             # Using "0123456789"
    call %In_Set                                # See if we have a match
    cmp_eax, !1                                 # IF match
    jne %primary_expr_fail                      # otherwise we failed hard

    # Deal with numbers
    call %primary_expr_number                   # Collect the number
    jmp %primary_expr_done                      # Be done

:primary_expr_fail
    # looks like we hit bad input
    # abort before it gets bad
    call %primary_expr_failure                  # No match means failure
:primary_expr_done
    pop_ebx                                     # Restore EBX
    ret

:primary_expr_string_0  "mov_eax, %0
"
:primary_expr_string_1  "sub_ebx,eax
mov_eax,ebx
"
:primary_expr_string_2  "mov_eax, %1
"
:primary_expr_string_3  "xor_eax,ebx
"
:primary_expr_string_4  "not_eax
"
:primary_expr_string_5  "Error in Primary expression
Didn't get )
"
:primary_expr_string_6  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
:primary_expr_string_7  "0123456789"


# primary_expr_variable function
# Receives nothing
# Returns nothing
# Walks global and updates output
# Uses EAX for struct token_list* a and ECX for char* S
:primary_expr_variable
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_ecx,[eax+BYTE] !8                       # S = global_token->S
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax,ecx                                 # Using S
    mov_ebx,[DWORD] &global_constant_list       # Using global_constant_list
    call %sym_lookup                            # sym_lookup(s, global_constant_list)
    cmp_eax, !0                                 # IF NULL == sym_lookup(s, global_constant_list)
    je %primary_expr_variable_local             # Try locals next

    # Deal with constant load
    mov_ebx,[eax+BYTE] !16                      # a->ARGS
    mov_eax, &primary_expr_variable_string_2    # Using "mov_eax, %"
    call %emit_out                              # Emit it

    mov_eax,[ebx+BYTE] !8                       # a->ARGS->S
    call %emit_out                              # Emit it

    mov_eax, &primary_expr_variable_string_1    # Using "\n"
    call %emit_out                              # Emit it
    jmp %primary_expr_variable_done             # Be done

:primary_expr_variable_local
    mov_eax,ecx                                 # Using S
    mov_ebx,[DWORD] &function                   # Using function
    mov_ebx,[ebx+BYTE] !4                       # function->locals
    call %sym_lookup                            # sym_lookup(s, function->locals)
    cmp_eax, !0                                 # IF NULL == sym_lookup(s, function->locals)
    je %primary_expr_variable_arguments         # try arguments next

    # Deal with local load
    call %variable_load                         # Collect it
    jmp %primary_expr_variable_done             # Be done

:primary_expr_variable_arguments
    mov_eax,ecx                                 # Using S
    mov_ebx,[DWORD] &function                   # Using function
    mov_ebx,[ebx+BYTE] !16                      # function->args
    call %sym_lookup                            # sym_lookup(s, function->args)
    cmp_eax, !0                                 # IF NULL == sym_lookup(s, global_constant_list)
    je %primary_expr_variable_function          # try functions next

    # Deal with argument load
    call %variable_load                         # Collect it
    jmp %primary_expr_variable_done             # Be done

:primary_expr_variable_function
    mov_eax,ecx                                 # Using S
    mov_ebx,[DWORD] &global_function_list       # Using global_function_list
    call %sym_lookup                            # sym_lookup(s, global_function_list)
    cmp_eax, !0                                 # IF NULL == sym_lookup(s, global_function_list)
    je %primary_expr_variable_global            # try globals next

    # Deal with functions
    call %function_load                         # Deal with the function
    jmp %primary_expr_variable_done             # Be done

:primary_expr_variable_global
    mov_eax,ecx                                 # Using S
    mov_ebx,[DWORD] &global_symbol_list         # Using global_symbol_list
    call %sym_lookup                            # sym_lookup(s, global_symbol_list)
    cmp_eax, !0                                 # IF NULL == sym_lookup(s, global_symbol_list)
    je %primary_expr_variable_error             # Give up

    # Deal with globals
    call %global_load                           # Collect that global
    jmp %primary_expr_variable_done             # Be done

:primary_expr_variable_error
    mov_eax, %2                                 # Using standard error
    mov_[DWORD],eax &Output_file                # write to standard error
#   call %line_error                            # Write useful debug info
    mov_eax,ecx                                 # put S in the right place
    call %File_Print                            # print it

    mov_eax, &primary_expr_variable_string_0    # Ending string
    call %File_Print                            # print it
    jmp %Exit_Failure                           # Abort Hard

:primary_expr_variable_done
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:primary_expr_variable_string_0  " is not a defined symbol
"
:primary_expr_variable_string_1  "
"
:primary_expr_variable_string_2  "mov_eax, %"


# function_call function
# Receives char* S in EAX and int BOOL in EBX
# Builds stack frames before and tears them down after function calls
# Uses ECX for char* S, EDX for int BOOL, ESI for PASSED
:function_call
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    push_esi                                    # Protect ESI
    mov_ecx,eax                                 # Put S in place
    mov_edx,ebx                                 # Put BOOL in place
    mov_esi, %0                                 # PASSED = 0

    mov_eax, &function_call_string_0            # Using "ERROR in process_expression_list\nNo ( was found\n"
    mov_ebx, &open_paren                        # Using "("
    call %require_match                         # Make sure we have it

    mov_eax, &function_call_string_1            # Using "push_edi\t# Prevent overwriting in recursion\n"
    call %emit_out                              # Emit it

    mov_eax, &function_call_string_2            # Using "push_ebp\t# Protect the old base pointer\n"
    call %emit_out                              # Emit it

    mov_eax, &function_call_string_3            # Using "mov_edi,esp\t# Copy new base pointer\n"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # Make it useful
    cmp_eax, !41                                # IF global_token->S[0] == ")"
    je %function_call_gen_done                  # Then no arguments to send

    # looks like we have arguments to collect
    call %expression                            # Collect the argument

    mov_eax, &function_call_string_4            # Using "push_eax\t#_process_expression1\n"
    call %emit_out                              # Emit it
    mov_esi, %1                                 # PASSED = 1

:function_call_gen_iter
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # Make it useful
    cmp_eax, !44                                # IF global_token->S[0] == ","
    jne %function_call_gen_done                 # Otherwise we are done

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    call %expression                            # Collect the argument

    mov_eax, &function_call_string_5            # Using "push_eax\t#_process_expression2\n"
    call %emit_out                              # Emit it
    add_esi, !1                                 # PASSED = PASSED + 1
    jmp %function_call_gen_iter                 # Keep trying

:function_call_gen_done
    # All is collected
    mov_eax, &function_call_string_6            # Using "ERROR in process_expression_list\nNo ) was found\n"
    mov_ebx, &close_paren                       # Using ")"
    call %require_match                         # Make sure we have it

    cmp_edx, !0                                 # IF(BOOL == TRUE)
    jne %function_call_static                   # Otherwise it is a static call

    # Deal with a passed function pointer
    mov_eax, &function_call_string_7            # Using "lea_eax,[ebp+DWORD] %"
    call %emit_out                              # Emit it

    mov_eax,ecx                                 # Using S
    call %emit_out                              # Emit it

    mov_eax, &function_call_string_8            # Using "\nmov_eax,[eax]\n"
    call %emit_out                              # Emit it

    mov_eax, &function_call_string_9            # Using "mov_ebp,edi\n"
    call %emit_out                              # Emit it

    mov_eax, &function_call_string_10           # Using "call_eax\n"
    call %emit_out                              # Emit it

    mov_eax, &function_call_string_13           # Using "pop_ebx\t# _process_expression_locals\n"
    jmp %function_call_cleanup                  # Clean up

:function_call_static
    # Deal with fixed function name
    mov_eax, &function_call_string_9            # Using "mov_ebp,edi\n"
    call %emit_out                              # Emit it

    mov_eax, &function_call_string_11           # Using "call %FUNCTION_"
    call %emit_out                              # Emit it

    mov_eax,ecx                                 # Using S
    call %emit_out                              # Emit it

    mov_eax, &function_call_string_12           # Using "\n"
    call %emit_out                              # Emit it

    mov_eax, &function_call_string_13           # Using "pop_ebx\t# _process_expression_locals\n"

:function_call_cleanup
    cmp_esi, !0                                 # IF PASSED > 0
    jle %function_call_done                     # Otherwise be done

    # The desired string is already in EAX
    call %emit_out                              # Emit it

    sub_esi, !1                                 # PASSED = PASSED - 1
    jmp %function_call_cleanup                  # Keep going

:function_call_done
    mov_eax, &function_call_string_14           # Using "pop_ebp\t# Restore old base pointer\n"
    call %emit_out                              # Emit it

    mov_eax, &function_call_string_15           # Using "pop_edi\t# Prevent overwrite\n"
    call %emit_out                              # Emit it

    pop_esi                                     # Restore ESI
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:function_call_string_0  "ERROR in process_expression_list
No ( was found
"
:function_call_string_1  "push_edi  # Prevent overwriting in recursion
"
:function_call_string_2  "push_ebp  # Protect the old base pointer
"
:function_call_string_3  "mov_edi,esp   # Copy new base pointer
"
:function_call_string_4  "push_eax  #_process_expression1
"
:function_call_string_5  "push_eax  #_process_expression2
"
:function_call_string_6  "ERROR in process_expression_list
No ) was found
"
:function_call_string_7  "lea_eax,[ebp+DWORD] %"
:function_call_string_8  "
mov_eax,[eax]
"
:function_call_string_9  "mov_ebp,edi
"
:function_call_string_10 "call_eax
"
:function_call_string_11 "call %FUNCTION_"
:function_call_string_12 "
"
:function_call_string_13 "pop_ebx   # _process_expression_locals
"
:function_call_string_14 "pop_ebp   # Restore old base pointer
"
:function_call_string_15 "pop_edi   # Prevent overwrite
"


# variable_load function
# Receives struct token_list* A in EAX
# Returns nothing
# Updates output and current_target
# Uses ECX for A
:variable_load
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ecx,eax                                 # Protect A

    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &open_paren                        # Using "("
    call %match                                 # IF global_token->S == "("
    cmp_eax, !0                                 # Then it might be a function
    jne %variable_load_regular                  # Otherwise it is regular

    mov_ebx,[ecx+BYTE] !12                      # A->TYPE
    mov_ebx,[ebx+BYTE] !24                      # A->TYPE->NAME
    mov_eax, &type_function_name                # Using "FUNCTION"
    call %match                                 # IF A->TYPE->NAME == "FUNCTION"
    cmp_eax, !0                                 # Then it must be a function
    jne %variable_load_regular                  # otherwise just another regular

    # deal with function
    mov_eax,[ecx+BYTE] !16                      # A->DEPTH
    call %numerate_number                       # Convert to string
    mov_ebx, %0                                 # pass 0 for true
    call %function_call                         # Create the function call
    jmp %variable_load_done                     # Be done

:variable_load_regular
    mov_eax,[ecx+BYTE] !12                      # A->TYPE
    mov_[DWORD],eax &current_target             # current_target = A->TYPE

    mov_eax, &variable_load_string_0            # Using "lea_eax,[ebp+DWORD] %"
    call %emit_out                              # Emit it

    mov_eax,[ecx+BYTE] !16                      # A->DEPTH
    call %numerate_number                       # Convert to string
    call %emit_out                              # Emit it

    mov_eax, &variable_load_string_1            # Using "\n"
    call %emit_out                              # Emit it

    # Check for special case of assignment
    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &equal                             # Using "="
    call %match                                 # IF global_token->S == "="
    cmp_eax, !0                                 # Then we skip loading
    je %variable_load_done                      # And be done

    # Deal with common case
    mov_eax, &variable_load_string_2            # Using "mov_eax,[eax]\n"
    call %emit_out                              # Emit it

:variable_load_done
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:variable_load_string_0  "lea_eax,[ebp+DWORD] %"
:variable_load_string_1  "
"
:variable_load_string_2  "mov_eax,[eax]
"


# function_load function
# Receives struct token_list* a in EAX
# Returns nothing
# Uses ECX to hold A->S
:function_load
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_eax,[eax+BYTE] !8                       # A->S
    mov_ecx,eax                                 # Protect A->S
    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &open_paren                        # Using "("
    call %match                                 # IF global_token->S == "("
    cmp_eax, !0                                 # The we need to do a function call
    jne %function_load_regular                  # Otherwise just load it's address

    # Deal with function call
    mov_eax,ecx                                 # Using A->S
    mov_ebx, %1                                 # Using FALSE
    call %function_call                         # Deal with it
    jmp %function_load_done                     # Be done

:function_load_regular
    mov_eax, &function_load_string_0            # Using "mov_eax, &FUNCTION_"
    call %emit_out                              # Emit it

    mov_eax,ecx                                 # Using A->S
    call %emit_out                              # Emit it

    mov_eax, &function_load_string_1            # Using "\n"
    call %emit_out                              # Emit it

:function_load_done
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:function_load_string_0  "mov_eax, &FUNCTION_"
:function_load_string_1  "
"


# global_load function
# Receives struct token_list* A in EAX
# Returns nothing
# Uses EBX to hold A->S
:global_load
    push_ebx                                    # Protect EBX
    mov_ebx,eax                                 # Set as A
    mov_ebx,[ebx+BYTE] !8                       # Set as A->S

    mov_eax,[eax+BYTE] !12                      # A->TYPE
    mov_[DWORD],eax &current_target             # current_target = A->TYPE

    mov_eax, &global_load_string_0              # Using "mov_eax, &GLOBAL_"
    call %emit_out                              # Emit it

    mov_eax,ebx                                 # Using A->S
    call %emit_out                              # Emit it

    mov_eax, &global_load_string_1              # Using "\n"
    call %emit_out                              # Emit it

    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &equal                             # "="
    call %match                                 # IF global_token->S == "="
    cmp_eax, !0                                 # We need to skip for assignment
    je %global_load_done                        # and be done

    # Otherwise we are loading the contents
    mov_eax, &global_load_string_2              # Using "mov_eax,[eax]\n"
    call %emit_out                              # Emit it

:global_load_done
    pop_ebx                                     # Restore EBX
    ret

:global_load_string_0  "mov_eax, &GLOBAL_"
:global_load_string_1  "
"
:global_load_string_2  "mov_eax,[eax]
"


# sym_lookup function
# Receives char* S in EAX and struct token_list* symbol_list in EBX
# Uses I->S in EAX, S in EBX and I in ECX
# Returns match or NULL
:sym_lookup
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ecx,ebx                                 # I = symbol_list
    mov_ebx,eax                                 # Put S in the right place
:sym_lookup_iter
    cmp_ecx, !0                                 # IF NULL == I
    je %sym_lookup_done                         # We failed to find match

    mov_eax,[ecx+BYTE] !8                       # Using I->S
    call %match                                 # IF I->S == S
    cmp_eax, !0                                 # then be done
    je %sym_lookup_done                         # Failed

    mov_ecx,[ecx]                               # I = I->NEXT
    jmp %sym_lookup_iter                        # otherwise keep looping

:sym_lookup_done
    mov_eax,ecx                                 # Return I
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# primary_expr_number function
# Receives nothing
# Returns nothing
# Simply uses current global token to update output and then steps to next global_token
:primary_expr_number
    mov_eax, &primary_expr_number_string_0      # Using "mov_eax, %"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    call %emit_out                              # Emit it

    mov_eax, &primary_expr_number_string_1      # Using "\n"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT
    ret

:primary_expr_number_string_0  "mov_eax, %"
:primary_expr_number_string_1  "
"


# primary_expr_string function
# receives nothing
# Returns nothing
# creates entries for string and calls to generate string output
# uses ECX for char* number_string
:primary_expr_string
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ebx,[DWORD] &current_count              # Using current_count
    mov_eax,ebx                                 # And putting it in the right place
    call %numerate_number                       # Get the string
    mov_ecx,eax                                 # protect number_string

    add_ebx, !1                                 # current_count + 1
    mov_[DWORD],ebx &current_count              # current_count = current_count + 1

    mov_eax, &primary_expr_string_string_0      # Using "mov_eax, &STRING_"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    mov_ebx,ecx                                 # Put number_string in the right place
    call %uniqueID_out                          # Make it unique

    # Generate the target
    mov_eax, &primary_expr_string_string_1      # Using ":STRING_"
    mov_ebx,[DWORD] &strings_list               # Using strings_list
    call %emit                                  # Emit it
    mov_ebx,eax                                 # put new strings_list in place

    mov_eax,[DWORD] &function                   # Using function
    mov_eax,[eax+BYTE] !8                       # function->S
    call %uniqueID                              # Make it unique
    mov_ebx,eax                                 # put new strings_list in place

    # Parse the string
    mov_eax,[DWORD] &global_token               # Using global token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    call %parse_string                          # convert to useful form
    call %emit                                  # Emit it
    mov_[DWORD],eax &strings_list               # Update Strings _list

    mov_eax,[DWORD] &global_token               # Using global token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:primary_expr_string_string_0  "mov_eax, &STRING_"
:primary_expr_string_string_1  ":STRING_"


# primary_expr_char function
# Receives nothing
# Returns nothing
# Updates output_list using global_token
:primary_expr_char
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_eax, &primary_expr_char_string_0        # Using "mov_eax, %"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &global_token               # Using global token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    add_eax, !1                                 # global_token->S + 1
    call %escape_lookup                         # Get the char
    call %numerate_number                       # Convert to string
    call %emit_out                              # emit it

    mov_eax, &primary_expr_char_string_1        # Using "\n"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &global_token               # Using global token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:primary_expr_char_string_0  "mov_eax, %"
:primary_expr_char_string_1  "
"


# primary_expr_failure function
# Receives nothing
# Does not return but aborts hard
# Complains about the bad input
:primary_expr_failure
#   call %line_error                            # Get line of issue
    mov_eax, %2                                 # Using Standard error
    mov_[DWORD],eax &Output_file                # write to standard error
    mov_eax, &primary_expr_failure_string_0     # Using "Received "
    call %File_Print                            # Print it

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    call %File_Print                            # Print it

    mov_eax, &primary_expr_failure_string_1     # Using " in primary_expr\n"
    call %File_Print                            # Print it
    jmp %Exit_Failure                           # Abort Hard

:primary_expr_failure_string_0  "Received "
:primary_expr_failure_string_1  " in primary_expr
"


# general_recursion function
# Receives FUNCTION F in EAX, char* S in EBX, char* name in ECX and FUNCTION iterate in EDX
# Returns nothing
# Uses ECX for char* S, EDX for FUNCTION iterate and EBP for FUNCTION F
# But generally recurses a shitload
:general_recursion
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    push_ebp                                    # Protect EBP
    mov_ebp,eax                                 # Protect F
    mov_eax,ecx                                 # Put name in the right place
    mov_ecx,ebx                                 # Protect S

    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    call %match                                 # IF match(name, global_token->s)
    cmp_eax, !0                                 # If true we do
    jne %general_recursion_done                 # Otherwise skip it

    # Deal with the recursion
    mov_eax,ebp                                 # Put F in the right place
    call %common_recursion                      # Recurse

    mov_eax,ecx                                 # Put S in the right place
    call %emit_out                              # Emit it

    mov_eax,edx                                 # Put iterate in the right place
    call_eax                                    # Down the rabbit hole

:general_recursion_done
    pop_ebp                                     # Restore EBP
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# promote_type function
# Receives struct type* a in EAX and struct type* b in EBX
# Returns the most recent type in EAX
# Uses EAX for struct type* I, ECX for struct type* A and EDX for struct type* B
:promote_type
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    cmp_ebx, !0                                 # IF NULL == B
    je %promote_type_done                       # Just return A

    mov_ecx,eax                                 # Put A in place
    mov_edx,ebx                                 # Put B in place
    mov_eax,ebx                                 # IF NULL == A
    cmp_ecx, !0                                 # Then we just return B
    je %promote_type_done                       # Be done

    # Looks like we need to walk the list
    mov_ecx,[ecx+BYTE] !24                      # A->NAME
    mov_edx,[edx+BYTE] !24                      # B->NAME
    mov_eax,[DWORD] &global_types               # I = global_types
:promote_type_iter
    cmp_eax, !0                                 # IF NULL == I
    je %promote_type_done                       # Just be done

    mov_ebx,[eax+BYTE] !24                      # I->NAME
    cmp_ebx,ecx                                 # IF(A->NAME == I->NAME)
    je %promote_type_done                       # Be done

    cmp_ebx,edx                                 # IF(B->NAME == I->NAME)
    je %promote_type_done                       # Be done

    mov_ebx,[eax+BYTE] !12                      # I->INDIRECT
    mov_ebx,[ebx+BYTE] !24                      # I->INDIRECT->NAME

    cmp_ebx,ecx                                 # IF(A->NAME == I->INDIRECT->NAME)
    je %promote_type_done                       # Be done

    cmp_ebx,edx                                 # IF(B->NAME == I->INDIRECT->NAME)
    je %promote_type_done                       # Be done

    mov_eax,[eax]                               # I = I->NEXT
    jmp %promote_type_iter                      # Keep going

:promote_type_done
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# common_recursion function
# Receives FUNCTION F in EAX
# Returns Nothing
# Walks global_token list and update output_list
# Updates current_target
# Uses EBX to hold FUNCTION F and struct type* last_type
:common_recursion
    push_ebx                                    # Protect EBX
    mov_ebx,eax                                 # Put FUNCTION F safely out of the way
    mov_eax, &common_recursion_string_0         # Using "push_eax\t#_common_recursion\n"
    call %emit_out                              # Emit it

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax,ebx                                 # Prepare for function call
    mov_ebx,[DWORD] &current_target             # Get last type
    call_eax                                    # F();
    mov_eax,[DWORD] &current_target             # Get current_target
    call %promote_type                          # get the right type
    mov_[DWORD],eax &current_target             # Set new current_target

    mov_eax, &common_recursion_string_1         # Using "pop_ebx\t# _common_recursion\n"
    call %emit_out                              # Emit it
    pop_ebx                                     # Restore EBX
    ret

:common_recursion_string_0  "push_eax   #_common_recursion
"
:common_recursion_string_1  "pop_ebx    # _common_recursion
"


# require_match function
# Receives char* message in EAX and char* required in EBX
# Returns nothing
# Uses ECX to hold message and updates global_token
:require_match
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ecx,eax                                 # put the message somewhere safe
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    call %match                                 # IF required == global_token->S
    cmp_eax, !0                                 # we are fine
    je %require_match_good                      # otherwise pain

    # Deal will bad times
#   call %line_error                            # Tell user what went wrong
    mov_eax, %2                                 # Using standard error
    mov_[DWORD],eax &Output_file                # write to standard error
    mov_eax,ecx                                 # using our message
    call %File_Print                            # Print it
    jmp %Exit_Failure                           # Abort HARD

:require_match_good
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->next
    mov_[DWORD],eax &global_token               # global_token = global_token->next
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# uniqueID Function
# Receives char *S in EAX, struct token_list* l in EBX and char* num in ECX
# Returns updated struct token_list* L in EAX
:uniqueID
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    call %emit                                  # emit(s, l)
    mov_ebx,eax                                 # Put L in correct place
    mov_eax, &underline                         # Using "_"
    call %emit                                  # emit("_", l)
    mov_ebx,eax                                 # Put L in correct place
    mov_eax,ecx                                 # Put num in correct place
    call %emit                                  # emit(num, l)
    mov_ebx,eax                                 # Put L in correct place
    mov_eax, &uniqueID_string_0                 # Using "\n"
    call %emit                                  # emit("\n", l)
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:uniqueID_string_0  "
"


# uniqueID_out function
# Receives char* S in EAX and char* num in EBX
# Returns nothing
:uniqueID_out
    push_eax                                    # Protect EAX
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ecx,ebx                                 # Put num in right spot
    mov_ebx,[DWORD] &output_list                # Using output_list
    call %uniqueID                              # Get updated list
    mov_[DWORD],eax &output_list                # output_list = uniqueID(s, output_list, num)
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    pop_eax                                     # Restore EAX
    ret


# emit_out function
# Receives char* S in EAX
# Returns nothing
# Updates output_list
# MUST NOT ALTER REGISTERS
:emit_out
    push_eax                                    # Protect EAX
    push_ebx                                    # Protect EBX
    mov_ebx,[DWORD] &output_list                # Using output_list
    call %emit                                  # emit it
    mov_[DWORD],eax &output_list                # update it
    pop_ebx                                     # Restore EBX
    pop_eax                                     # Restore EAX
    ret


# emit function
# Receives char *s in EAX and struct token_list* head in EBX
# Returns struct token_list* T in EAX
:emit
    push_ecx                                    # Protect ECX
    mov_ecx,eax                                 # put S out of the way
    mov_eax, %20                                # sizeof(struct token_list)
    call %malloc                                # get T
    mov_[eax],ebx                               # t->next = head;
    mov_[eax+BYTE],ecx !8                       # t->s = s;
    pop_ecx                                     # Restore ECX
    ret


# escape_lookup function
# Receives char* c in EAX
# Returns integer value of char in EAX
# Aborts hard if unknown escape is received
# Uses ECX to hold char* C
:escape_lookup
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ecx,eax                                 # Put char* C in safe place
    mov_al,[ecx]                                # Load c[0]
    movzx_eax,al                                # make it useful
    cmp_eax, !92                                # If '\\' != c[0]
    jne %escape_lookup_done                     # Be done

    mov_ebx,ecx                                 # Prepare for walk
    add_ebx, !1                                 # increment
    mov_bl,[ebx]                                # load c[1]
    movzx_ebx,bl                                # make it useful

    cmp_ebx, !120                               # Check if \x??
    je %escape_lookup_hex                       # Deal with hex

    # Deal with \? escapes
    mov_eax, %10                                # Guess "\n"
    cmp_ebx, !110                               # If n
    je %escape_lookup_done                      # Be done

    mov_eax, %9                                 # Guess "\t"
    cmp_ebx, !116                               # If t
    je %escape_lookup_done                      # Be done

    mov_eax,ebx                                 # "\\", "'" and '\"' all encode as themselves
    cmp_ebx, !92                                # If "\\"
    je %escape_lookup_done                      # Be done
    cmp_ebx, !39                                # IF "'"
    je %escape_lookup_done                      # Be done
    cmp_ebx, !34                                # IF '\"'
    je %escape_lookup_done                      # Be done

    mov_eax, %13                                # Guess "\r"
    cmp_ebx, !114                               # IF r
    je %escape_lookup_done                      # Be done

    # Looks like we have no clue what we are doing
    # Aborting hard
    mov_eax, %2                                 # Using Standard error
    mov_[DWORD],eax &Output_file                # write to standard error
    mov_eax, &escape_lookup_string_0            # Using "Unknown escape received: "
    call %File_Print                            # Print it
    mov_eax,ecx                                 # Using C
    call %File_Print                            # Print it
    mov_eax, &escape_lookup_string_1            # Using " Unable to process\n"
    call %File_Print                            # Print it
    jmp %Exit_Failure                           # Abort Hard

:escape_lookup_done
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:escape_lookup_hex
    # Give up on C and just assume they know what they are doing
    add_ecx, !2                                 # increment
    mov_al,[ecx]                                # c[2]
    movzx_eax,al                                # make it useful
    add_ecx, !1                                 # increment
    call %char2hex                              # Get the hex value
    sal_eax, !4                                 # c << 4
    mov_bl,[ecx]                                # c[3]
    movzx_ebx,bl                                # make it useful
    xchg_eax,ebx                                # protect c << 4
    call %char2hex                              # Get the hex value
    add_eax,ebx                                 # hex(c[2]) << 4 + hex(c[3])
    jmp %escape_lookup_done                     # Be done

:escape_lookup_string_0  "Unknown escape received: "
:escape_lookup_string_1  " Unable to process
"


# char2hex function
# Receives char in EAX
# Returns hex or aborts hard
:char2hex
    sub_eax, !48                                # Try 0-9
    cmp_eax, !10                                # Otherwise fun times
    jl %char2hex_done                           # Be done

    # Deal with A-F
    and_eax, %0xDF                              # Unset High bit turning a-f into A-F
    sub_eax, !7                                 # Shift down into position
    cmp_eax, !10                                # Everything below A is bad
    jl %char2hex_fail                           # And should fail
    cmp_eax, !16                                # Make sure we are below F
    jl %char2hex_done                           # If so be done

:char2hex_fail
    # Time to fail hard
    mov_eax, %2                                 # Using Standard error
    mov_[DWORD],eax &Output_file                # write to standard error
    mov_eax, &char2hex_string_0                 # Using "Tried to print non-hex number\n"
    call %File_Print                            # Print it
    jmp %Exit_Failure                           # Abort Hard

:char2hex_done
    ret

:char2hex_string_0  "Tried to print non-hex number
"


# parse_string function
# Receives char* string in EAX
# Returns cleaned up string
# Protects char* string in EBX
:parse_string
    push_ebx                                    # Protect EBX
    mov_ebx,eax                                 # Protect char* string
    call %weird                                 # Determine if we have a weird string
    cmp_eax, !0                                 # If weird
    je %parse_string_weird                      # Deal with it

    # Dealing with regular string
    mov_eax,ebx                                 # Passing Char* string
    call %collect_regular_string                # Collect it
    jmp %parse_string_done                      # Be done

:parse_string_weird
    mov_eax,ebx                                 # Passing Char* string
    call %collect_weird_string                  # Collect it

:parse_string_done
    pop_ebx                                     # Restore EBX
    ret


# weird function
# Receives char* string in EAX
# Returns true(0) or false(1) in EAX
# Uses ECX to hold char* string
:weird
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ecx,eax                                 # Place string in safe place
    add_ecx, !1                                 # increment past the '\"'
:weird_reset
    mov_al,[ecx]                                # Load a char
    movzx_eax,al                                # Make it useful
    cmp_eax, !0                                 # IF NULL == C
    je %weird_false                             # Nothing weird found

    cmp_eax, !92                                # IF '\\'
    jne %weird_escaped                          # Deal with escaping

    # Deal with escape
    mov_eax,ecx                                 # We are passing the string
    call %escape_lookup                         # to look it up

    add_ecx, !1                                 # string = string + 1
    mov_bl,[ecx]                                # get string[1]
    movzx_ebx,bl                                # make it useful
    cmp_ebx, !120                               # IF 'x' == string[1]
    jne %weird_escaped                          # otherwise skip the gap

    add_ecx, !2                                 # string = string + 2

:weird_escaped
    push_eax                                    # Protect C in case we need it
    mov_ebx, &weird_string_0                    # Use "\t\n !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
    call %In_Set                                # To find if weird
    cmp_eax, !1                                 # IF TRUE
    pop_eax                                     # Restore C
    jne %weird_true                             # Then not weird

    add_ecx, !1                                 # string = string + 1

    # Last chance for weird
    mov_ebx, &weird_string_1                    # Use "\t\n\r "
    call %In_Set                                # Check for special case
    cmp_eax, !1                                 # IF TRUE
    jne %weird_reset                            # Otherwise not in the special case

    # Deal with possible special case
    mov_al,[ecx]                                # Load string[1]
    movzx_eax,al                                # Make it useful
    cmp_eax, !58                                # IF string[1] == ":"
    je %weird_true                              # Then we hit the special case
    jmp %weird_reset                            # Keep trying

:weird_done
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:weird_true
    mov_eax, %0                                 # Return true
    jmp %weird_done                             # Be done

:weird_false
    mov_eax, %1                                 # Return false
    jmp %weird_done                             # Be done

:weird_string_0  "  
 !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
:weird_string_1  '09 0A 0D 20' ; "\t\n\r "


# collect_regular_string function
# Receives char* string in EAX
# Malloc and creates new string to return in EAX
# Uses ECX for return string and EDX for passed string
:collect_regular_string
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    mov_edx,eax                                 # Protect our passed string
    mov_eax, %256                               # We need 256 bytes of storage
    call %malloc                                # Get our new pointer
    mov_ecx,eax                                 # put it in place
    push_eax                                    # protect until done
:collect_regular_string_reset
    mov_al,[edx]                                # string[0]
    movzx_eax,al                                # Make it useful
    cmp_eax, !0                                 # See if we hit the end
    je %collect_regular_string_done             # And be done

    cmp_eax, !92                                # IF string[0] == '\\'
    je %collect_regular_string_escaped          # Deal with that mess

    # deal with boring char
    mov_[ecx],al                                # hold_string[index] = string[0]
    add_ecx, !1                                 # Increment it
    add_edx, !1                                 # Increment it
    jmp %collect_regular_string_reset           # And keep going

:collect_regular_string_escaped
    mov_eax,edx                                 # Using string
    call %escape_lookup                         # Get the char
    mov_[ecx],al                                # hold_string[index] = escape_lookup(string)
    add_edx, !1                                 # Increment it
    add_ecx, !1                                 # Increment it
    mov_al,[edx]                                # string[0]
    movzx_eax,al                                # Make it useful
    add_edx, !1                                 # Increment it
    cmp_eax, !120                               # IF 'x' == string[1]
    jne %collect_regular_string_reset           # Otherwise keep going

    add_edx, !2                                 # Increment it
    jmp %collect_regular_string_reset           # Keep going

:collect_regular_string_done
    mov_eax, %34                                # Using '\"'
    mov_[ecx],al                                # hold_string[index] = '\"'
    add_ecx, !1                                 # Increment it
    mov_eax, %10                                # Using "\n"
    mov_[ecx],al                                # hold_string[index] = '\n'
    pop_eax                                     # Return our new string
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# collect_weird_string function
# Receives char* string in EAX
# Mallocs and returns char* hold in EAX
# Uses ECX for char* hold and EDX for char* string
:collect_weird_string
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    mov_edx,eax                                 # Protect our passed string
    mov_eax, %512                               # We need 512 bytes of storage
    call %malloc                                # Get our new pointer
    mov_ecx,eax                                 # put it in place
    push_eax                                    # protect until done

    mov_eax, %39                                # Using "'"
    mov_[ecx],al                                # hold_string[index] = "'"
    add_ecx, !1                                 # Increment it
    add_edx, !1                                 # Increment it
:collect_weird_string_reset
    mov_al,[edx]                                # Read a byte
    movzx_eax,al                                # Make it useful
    cmp_eax, !0                                 # IF NULL == string[0]
    je %collect_weird_string_done               # Be done

    mov_eax, %32                                # Using ' '
    mov_[ecx],al                                # hold_string[index] = ' '
    add_ecx, !1                                 # Increment it

    mov_eax,edx                                 # Using string
    call %escape_lookup                         # Get the char
    call %hex8                                  # Update ECX

    mov_al,[edx]                                # Read a byte
    movzx_eax,al                                # Make it useful
    add_edx, !1                                 # Increment it
    cmp_eax, !92                                # IF string[0] == '\\'
    jne %collect_weird_string_reset             # Otherwise keep going

    mov_al,[edx]                                # Read a byte
    movzx_eax,al                                # Make it useful
    add_edx, !1                                 # Increment it
    cmp_eax, !120                               # IF 'x' == string[1]
    jne %collect_weird_string_reset             # Otherwise keep going

    add_edx, !2                                 # Increment it
    jmp %collect_weird_string_reset             # Keep going

:collect_weird_string_done
    mov_eax, %32                                # Using ' '
    mov_[ecx],al                                # hold_string[index] = ' '
    add_ecx, !1                                 # Increment it
    mov_eax, %48                                # Using '0'
    mov_[ecx],al                                # hold_string[index] = '0'
    add_ecx, !1                                 # Increment it
    mov_[ecx],al                                # hold_string[index] = '0'
    add_ecx, !1                                 # Increment it
    mov_eax, %39                                # Using "'"
    mov_[ecx],al                                # hold_string[index] = "'"
    add_ecx, !1                                 # Increment it
    mov_eax, %10                                # Using "\n"
    mov_[ecx],al                                # hold_string[index] = '\n'
    pop_eax                                     # Return our new string
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# HEX to ascii routine
# Receives INT in EAX and CHAR* in ECX
# Stores ascii of INT in CHAR*
# Returns only modifying EAX and ECX
:hex8
    push_eax                                    # Protect bottom nibble
    shr_eax, !4                                 # do high nibble first
    call %hex4                                  # Store it
    pop_eax                                     # do low nibble
:hex4
    and_eax, %0xF                               # isolate nibble
    add_eax, !48                                # convert to ascii
    cmp_eax, !57                                # valid digit?
    jbe8 !hex1                                  # yes
    add_eax, !7                                 # use alpha range
:hex1
    mov_[ecx],al                                # store result
    add_ecx, !1                                 # next position
    ret


# type_name function
# Receives nothing
# Returns type_size in EAX
# Uses ECX for STRUCT TYPE* RET
:type_name
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx+BYTE] !8                       # global_token->S
    mov_eax, &struct                            # Using "struct"
    call %match                                 # IF global_token->S == "struct"
    mov_ecx,eax                                 # Protect structure
    cmp_eax, !0                                 # need to skip over "struct"
    jne %type_name_native                       # otherwise keep going

    # Deal with possible STRUCTs
    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx]                               # global_token->next
    mov_[DWORD],ebx &global_token               # global_token = global_token->next
    mov_eax,[ebx+BYTE] !8                       # global_token->S
    mov_ebx,[DWORD] &global_types               # get all known types
    call %lookup_type                           # Find type if possible
    mov_ecx,eax                                 # Set ret

    cmp_eax, !0                                 # IF NULL == ret
    jne %type_name_common                       # We have to create struct

    # Create a struct
    call %create_struct                         # Create a new struct
    mov_ecx, %0                                 # We wish to return NULL
    jmp %type_name_done                         # be done

:type_name_native
    # Deal only with native types
    mov_eax,ebx                                 # Put global_token->S in the right place
    mov_ebx,[DWORD] &global_types               # get all known types
    call %lookup_type                           # Find the type if possible
    mov_ecx,eax                                 # Set ret

    cmp_eax, !0                                 # IF NULL == ret
    jne %type_name_common                       # We need to abort hard

    # Aborting hard
    mov_eax, %2                                 # Using Standard error
    mov_[DWORD],eax &Output_file                # write to standard error
    mov_eax, &type_name_string_0                # Print header
    call %File_Print                            # Print it

    mov_eax,[DWORD] &global_token               # Using global token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    call %File_Print                            # Print it

    mov_eax, &type_name_string_1                # Print footer
    call %File_Print                            # Print it

#   call %line_error                            # Give details
    jmp %Exit_Failure                           # Abort

:type_name_common
    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx]                               # global_token->next
    mov_[DWORD],ebx &global_token               # global_token = global_token->next

:type_name_iter
    mov_eax,[ebx+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # make it useful
    cmp_eax, !42                                # IF global_token->S[0] == '*'
    jne %type_name_done                         # recurse

    # Deal with char**
    mov_ecx,[ecx+BYTE] !12                      # ret = ret->indirect
    mov_ebx,[DWORD] &global_token               # Using global_token
    mov_ebx,[ebx]                               # global_token->next
    mov_[DWORD],ebx &global_token               # global_token = global_token->next
    jmp %type_name_iter                         # keep looping

:type_name_done
    mov_eax,ecx                                 # put ret in the right place
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:type_name_string_0  "Unknown type "
:type_name_string_1  "
"


# lookup_type function
# Receives char* s in EAX and struct type* start in EBX
# Returns struct type* in EAX
# Uses EBX for S and ECX for I
:lookup_type
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    mov_ecx,ebx                                 # I = Start
    mov_ebx,eax                                 # Put S in place
:lookup_type_iter
    cmp_ecx, !0                                 # Check if I == NULL
    je %lookup_type_done                        # return NULL

    mov_eax,[ecx+BYTE] !24                      # I->NAME
    call %match                                 # Check if matching
    cmp_eax, !0                                 # IF I->NAME == S
    je %lookup_type_done                        # return it

    mov_ecx,[ecx]                               # Otherwise I = I->NEXT
    jmp %lookup_type_iter                       # And keep looping

:lookup_type_done
    mov_eax,ecx                                 # return either I or NULL
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# create_struct function
# Receives nothing
# Returns nothing
# Uses global_token to malloc a struct's definition
# Uses ECX for int OFFSET, EDX for struct type* head, EBP for struct type* I,
# EDI for member_size (Which is passed) and ESI for LAST
# EAX and EBX are used for scratch
:create_struct
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    push_ebp                                    # Protect EBP
    push_edi                                    # Protect EDI
    push_esi                                    # Protect ESI
    mov_ecx, %0                                 # OFFSET = 0
    mov_edi, %0                                 # member_size = 0

    mov_eax, %28                                # sizeof(struct type)
    call %malloc                                # malloc(sizeof(struct type))
    mov_edx,eax                                 # Set HEAD

    mov_eax, %28                                # sizeof(struct type)
    call %malloc                                # malloc(sizeof(struct type))
    mov_ebp,eax                                 # Set I

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_[edx+BYTE],eax !24                      # HEAD->NAME = global_token->S
    mov_[ebp+BYTE],eax !24                      # I->NAME = global_token->S

    mov_[edx+BYTE],ebp !12                      # HEAD->INDIRECT = I
    mov_[ebp+BYTE],edx !12                      # I->INDIRECT = HEAD

    mov_eax,[DWORD] &global_types               # Using global_types
    mov_[edx],eax                               # HEAD->NEXT = global_types
    mov_[DWORD],edx &global_types               # global_types = HEAD

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax, %4                                 # Using register size
    mov_[ebp+BYTE],eax !4                       # I->SIZE = register size

    mov_eax, &create_struct_string_0            # Using "ERROR in create_struct\n Missing {\n"
    mov_ebx, &open_curly_brace                  # Using "{"
    call %require_match                         # Make sure we have it

    mov_esi, %0                                 # LAST = NULL

:create_struct_iter
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # Make it useful
    cmp_eax, !125                               # IF global_token->S[0] == "}"
    je %create_struct_done                      # be done

    # Looks like we are adding members
    # Lets see if it is a union
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_ebx, &union                             # Using "union"
    call %match                                 # IF match(global_token->s, "union")
    cmp_eax, !0                                 # Deal with union
    jne %create_struct_single                   # Otherwise deal with singles

    # Deal with union
    mov_eax,esi                                 # Put last in right place
    mov_ebx,ecx                                 # put offset in right place
    call %build_union                           # ASSEMBLE
    mov_esi,eax                                 # last = build_union(last, offset)
    add_ecx,edi                                 # offset = offset + member_size

    mov_eax, &create_struct_string_1            # Using "ERROR in create_struct\n Missing ;\n"
    mov_ebx, &semicolon                         # Using ";"
    call %require_match                         # Make sure we have it
    jmp %create_struct_iter                     # keep going

:create_struct_single
    # deal with singles
    mov_eax,esi                                 # Put last in right place
    mov_ebx,ecx                                 # put offset in right place
    call %build_member                          # ASSEMBLE
    mov_esi,eax                                 # last = build_union(last, offset)
    add_ecx,edi                                 # offset = offset + member_size

    mov_eax, &create_struct_string_1            # Using "ERROR in create_struct\n Missing ;\n"
    mov_ebx, &semicolon                         # Using ";"
    call %require_match                         # Make sure we have it
    jmp %create_struct_iter                     # keep going

:create_struct_done
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax, &create_struct_string_1            # Using "ERROR in create_struct\n Missing ;\n"
    mov_ebx, &semicolon                         # Using ";"
    call %require_match                         # Make sure we have it

    mov_[edx+BYTE],ecx !4                       # HEAD->SIZE = OFFSET
    mov_[edx+BYTE],esi !16                      # HEAD->MEMBERS = LAST
    mov_[ebp+BYTE],esi !16                      # I->MEMBERS = LAST

    pop_esi                                     # Restore ESI
    pop_edi                                     # Restore EDI
    pop_ebp                                     # Restore EBP
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:create_struct_string_0  "ERROR in create_struct
 Missing {
"
:create_struct_string_1  "ERROR in create_struct
 Missing ;
"


# lookup_member function
# Receives struct type* parent in EAX and char* name in EBX
# Returns struct type* I in EAX
# Uses char* NAME in EBX, ECX for struct type* I and EDX to hold parent for errors
# Aborts hard if not found
:lookup_member
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    mov_edx,eax                                 # Protect Parent
    mov_ecx,[eax+BYTE] !16                      # struct type* I = parent->MEMBERS
:lookup_member_iter
    cmp_ecx, !0                                 # IF I == NULL
    je %lookup_member_fail                      # Abort HARD

    mov_eax,[ecx+BYTE] !24                      # Using I->NAME
    call %match                                 # IF I->NAME == NAME
    cmp_eax, !0                                 # Then we have found the member
    mov_eax,ecx                                 # Prepare for return
    mov_ecx,[ecx+BYTE] !16                      # Prepare for loop I = I->MEMBERS
    jne %lookup_member_iter                     # Looks like we are looping

    # I is already in EAX
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:lookup_member_fail
    mov_eax, %2                                 # Using Standard error
    mov_[DWORD],eax &Output_file                # write to standard error
    mov_eax, &lookup_member_string_0            # Using "ERROR in lookup_member "
    call %File_Print                            # print it

    mov_eax,[edx+BYTE] !24                      # PARENT->NAME
    call %File_Print                            # print it

    mov_eax, &arrow_string                      # Using "->"
    call %File_Print                            # print it

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    call %File_Print                            # print it

    mov_eax, &lookup_member_string_1            # Using " does not exist\n"
    call %File_Print                            # print it

#   call %line_error                            # Write useful debug info

    mov_eax, &lookup_member_string_2            # Using "\n"
    call %File_Print                            # print it
    jmp %Exit_Failure                           # Abort Hard

:lookup_member_string_0  "ERROR in lookup_member "
:lookup_member_string_1  " does not exist
"
:lookup_member_string_2  "
"


# build_member function
# Receives struct type* last in EAX, int offset in EBX and global member_size in EDI
# Updates member_size in EDI and returns struct type* I in EAX
# Uses ECX for struct type* member_type and EDX for struct type* I
:build_member
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    mov_edx,eax                                 # Put last out of the way
    mov_eax, %28                                # Allocate type
    call %malloc                                # Get I
    mov_[eax+BYTE],edx !16                      # I->MEMBERS = LAST
    mov_[eax+BYTE],ebx !8                       # I->OFFSET = OFFSET
    mov_edx,eax                                 # Put I in place

    call %type_name                             # Get member_type
    mov_ecx,eax                                 # Put in place
    mov_[edx+BYTE],ecx !20                      # I->TYPE = MEMBER_TYPE
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_ebx,[eax+BYTE] !8                       # global_token->S
    mov_[edx+BYTE],ebx !24                      # I->NAME = global_token->S
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    # Check if we have an array
    mov_ebx,[eax+BYTE] !8                       # global_token->S
    mov_eax, &open_bracket                      # Using "["
    call %match                                 # IF global_token->S == "["
    cmp_eax, !0                                 # Then we have to deal with arrays in our structs
    je %build_member_array                      # So deal with that pain

    # Deal with non-array case
    mov_eax,[ecx+BYTE] !4                       # member_type->SIZE
    mov_[edx+BYTE],eax !4                       # I->SIZE = member_type->SIZE
    jmp %build_member_done                      # Be done

:build_member_array
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax,[eax+BYTE] !8                       # global_token->S
    call %numerate_string                       # convert number
    mov_ebx,[ecx+BYTE] !20                      # member_type->TYPE
    mov_ebx,[ebx+BYTE] !4                       # member_type->TYPE->SIZE
    imul_eax,ebx                                # member_type->type->size * numerate_string(global_token->s)
    mov_[edx+BYTE],eax !4                       # I->SIZE = member_type->type->size * numerate_string(global_token->s)

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax, &build_member_string_0             # Using "Struct only supports [num] form\n"
    mov_ebx, &close_bracket                     # Using "]"
    call %require_match                         # Make sure we have it

:build_member_done
    mov_edi,[edx+BYTE] !4                       # MEMBER_SIZE = I->SIZE
    mov_[edx+BYTE],ecx !20                      # I->TYPE = MEMBER_TYPE
    mov_eax,edx                                 # Return I

    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:build_member_string_0  "Struct only supports [num] form
"


# build_union function
# Receives struct type* last in EAX, int offset in EBX and global member_size in EDI
# Updates member_size in EDI and returns struct type* LAST in EAX
# Uses ECX for struct type* last, EDX for int offset, ESI for int size and EDI for int member_size
:build_union
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    push_esi                                    # Protect ESI
    mov_ecx,eax                                 # Put LAST in right spot
    mov_edx,ebx                                 # Put OFFSET in right spot
    mov_esi, %0                                 # SIZE = 0

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax, &build_union_string_0              # Using "ERROR in build_union\nMissing {\n"
    mov_ebx, &open_curly_brace                  # Using "{"
    call %require_match                         # Make sure we have it

:build_union_iter
    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax+BYTE] !8                       # global_token->S
    mov_al,[eax]                                # global_token->S[0]
    movzx_eax,al                                # make it useful
    cmp_eax, !125                               # IF global_token->S[0] == "}"
    je %build_union_done                        # Be done

    # Collect union member
    mov_eax,ecx                                 # Passing LAST
    mov_ebx,edx                                 # Passing offset
    call %build_member                          # build_member(last, offset)
    mov_ecx,eax                                 # last = build_member(last, offset)

    cmp_esi,edi                                 # IF member_size > size
    jg %build_union_size                        # Then update size

    # deal with member_size > size
    mov_esi,edi                                 # SIZE = MEMBER_SIZE

:build_union_size
    mov_eax, &build_union_string_1              # Using "ERROR in build_union\nMissing ;\n"
    mov_ebx, &semicolon                         # Using ";"
    call %require_match                         # Make sure we have it
    jmp %build_union_iter                       # Keep going

:build_union_done
    mov_edi,esi                                 # MEMBER_SIZE = SIZE

    mov_eax,[DWORD] &global_token               # Using global_token
    mov_eax,[eax]                               # global_token->NEXT
    mov_[DWORD],eax &global_token               # global_token = global_token->NEXT

    mov_eax,ecx                                 # Return last

    pop_esi                                     # Restore ESI
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:build_union_string_0  "ERROR in build_union
Missing {
"
:build_union_string_1  "ERROR in build_union
Missing ;
"


# sym_declare function
# Receives char *s in EAX, struct type* t in EBX, and struct token_list* list in ECX
# Returns struct token_list* in EAX
# Uses EAX for A
:sym_declare
    push_edx                                    # Protect EDX
    mov_edx,eax                                 # Get char *S safely out of the way
    mov_eax, %20                                # Using sizeof(struct token_list)
    call %malloc                                # Get pointer to A
    mov_[eax],ecx                               # A->NEXT = LIST
    mov_[eax+BYTE],edx !8                       # A->S = S
    mov_[eax+BYTE],ebx !12                      # A->TYPE = T
    pop_edx                                     # Restore EDX
    ret


# match function
# Receives CHAR* in EAX and CHAR* in EBX
# Returns 0 (TRUE) or 1 (FALSE) in EAX
:match
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    mov_ecx,eax                                 # S1 in place
    mov_edx,ebx                                 # S2 in place
:match_Loop
    mov_al,[ecx]                                # S1[0]
    movzx_eax,al                                # Make it useful
    mov_bl,[edx]                                # S2[0]
    movzx_ebx,bl                                # Make it useful
    cmp_eax,ebx                                 # See if they match
    jne %match_False                            # If not

    add_ecx, !1                                 # S1 = S1 + 1
    add_edx, !1                                 # S2 = S2 + 1
    cmp_eax, !0                                 # If reached end of string
    je %match_Done                              # Perfect match
    jmp %match_Loop                             # Otherwise keep looping

:match_False
    mov_eax, %1                                 # Return false
:match_Done
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# numerate_number function
# Receives an INT A in EAX
# Returns char* result in EAX
# Allocates 16 bytes of memory
# Behaves badly when given a negative number too large
# Uses EAX for temp, EBX for DIVISOR, EDX for mod/0, ESI for result[i] and EBP for A
:numerate_number
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    push_esi                                    # Protect ESI
    push_ebp                                    # Protect EBP
    mov_ebp,eax                                 # Protect A

    mov_eax, %16                                # 16 bytes
    call %malloc                                # Get our pointer
    push_eax                                    # Protect our pointer
    mov_esi,eax                                 # put pointer in right place
    mov_ebx, %0x3B9ACA00                        # Set divisor to largest positive number that fits in 32bits

    cmp_ebp, !0                                 # Deal with 0 case
    je %numerate_number_ZERO                    # If it is
    jg %numerate_number_positive                # If it is positive

    # Deal with negative case
    mov_eax, %45                                # Using "-"
    mov_[esi],al                                # Write it
    add_esi, !1                                 # increment
    imul_ebp, !-1                               # A = A * -1

:numerate_number_positive
    mov_edx, %0                                 # Set top to 0
    mov_eax,ebp                                 # Using A as bottom
    idiv_ebx                                    # edx:eax % ebx -> edx + edx:eax / ebx -> eax [Even if we don't want it]
    cmp_eax, !0                                 # IF 0 == (a / divisor)
    jne %numerate_number_iter                   # Clean up those leading Zeros

    mov_edx, %0                                 # Set top to 0
    mov_eax,ebx                                 # Using Divisor for bottom
    mov_ebx, %10                                # Make this shit work because idiv 10 doesn't work
    idiv_ebx                                    # edx:eax % 10 -> edx + edx:eax / 10 -> eax [Even if we don't want it]
    mov_ebx,eax                                 # Update divisor
    jmp %numerate_number_positive               # Keep collecting

:numerate_number_iter
    cmp_ebx, !0                                 # IF DIVISOR < 0
    jle %numerate_number_done                   # Be done

    mov_edx, %0                                 # Set top to 0
    mov_eax,ebp                                 # Using A as bottom
    idiv_ebx                                    # edx:eax % ebx -> edx + edx:eax / ebx -> eax [Even if we don't want it]
    add_eax, !48                                # ((a / divisor) + 48)
    mov_[esi],al                                # Write it
    mov_ebp,edx                                 # a = a % divisor

    mov_edx, %0                                 # Set top to 0
    mov_eax,ebx                                 # Using Divisor for bottom
    mov_ebx, %10                                # Make this shit work because idiv 10 doesn't work
    idiv_ebx                                    # edx:eax % 10 -> edx + edx:eax / 10 -> eax [Even if we don't want it]
    mov_ebx,eax                                 # Update divisor

    add_esi, !1                                 # increment
    jmp %numerate_number_iter                   # Keep going

:numerate_number_done
    pop_eax                                     # Restore our result
    pop_ebp                                     # Restore EBP
    pop_esi                                     # Restore ESI
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret

:numerate_number_ZERO
    mov_eax, %48                                # Using '0'
    mov_[esi],al                                # Write it
    add_esi, !1                                 # increment
    jmp %numerate_number_done                   # Be done


# numerate_string function
# Receives CHAR* in EAX
# Returns value of CHAR* in EAX
# Uses EAX for VALUE, EBX for S, ECX for CH and ESI for NEGATIVE?
:numerate_string
    push_ebx                                    # Protect EBX
    push_ecx                                    # Protect ECX
    push_edx                                    # Protect EDX
    push_esi                                    # Protect ESI
    mov_ebx,eax                                 # put S in correct place
    mov_eax, %0                                 # Initialize to Zero
:numerate_string_loop
    mov_cl,[ebx+BYTE] !1                        # S[1]
    movzx_ecx,cl                                # make it useful
    cmp_ecx, !120                               # IF 'x' == S[1]
    je %numerate_hex                            # Deal with hex input

    # Assume decimal input
    mov_esi, %0                                 # Assume no negation
    mov_cl,[ebx]                                # S[0]
    movzx_ecx,cl                                # make it useful
    cmp_ecx, !45                                # IF '-' == S[0]
    jne %numerate_decimal                       # Skip negation

    mov_esi, %1                                 # Set FLAG
    add_ebx, !1                                 # S = S + 1

:numerate_decimal
    mov_cl,[ebx]                                # S[0]
    movzx_ecx,cl                                # make it useful
    cmp_ecx, !0                                 # IF NULL == S[0]
    je %numerate_decimal_done                   # We are done

    imul_eax, !10                               # VALUE = VALUE * 10
    sub_ecx, !48                                # CH = CH - '0'
    cmp_ecx, !9                                 # Check for illegal
    jg %numerate_string_fail                    # If CH > '9'
    cmp_ecx, !0                                 # Check for illegal
    jl %numerate_string_fail                    # IF CH < 0
    add_eax,ecx                                 # VALUE = VALUE + CH
    add_ebx, !1                                 # S = S + 1
    jmp %numerate_decimal                       # Keep looping

:numerate_decimal_done
    cmp_esi, !1                                 # Check if need to negate
    jne %numerate_string_done                   # Nope

    imul_eax, !-1                               # VALUE = VALUE * -1
    jmp %numerate_string_done                   # Done

:numerate_hex
    add_ebx, !2                                 # S = S + 2
:numerate_hex_loop
    mov_cl,[ebx]                                # S[0]
    movzx_ecx,cl                                # make it useful
    cmp_ecx, !0                                 # IF NULL == S[0]
    je %numerate_string_done                    # We are done

    sal_eax, !4                                 # VALUE = VALUE << 4
    sub_ecx, !48                                # CH = CH - '0'
    cmp_ecx, !10                                # IF 10 >= CH
    jl %numerate_hex_digit                      # NO
    sub_ecx, !7                                 # Push A-F into range
:numerate_hex_digit
    cmp_ecx, !15                                # Check for illegal
    jg %numerate_string_fail                    # If CH > 'F'
    cmp_ecx, !0                                 # Check for illegal
    jl %numerate_string_fail                    # IF CH < 0
    add_eax,ecx                                 # VALUE = VALUE + CH
    add_ebx, !1                                 # S = S + 1
    jmp %numerate_hex_loop                      # Keep looping

:numerate_string_fail
    mov_eax, %0                                 # return ZERO

:numerate_string_done
    pop_esi                                     # Restore ESI
    pop_edx                                     # Restore EDX
    pop_ecx                                     # Restore ECX
    pop_ebx                                     # Restore EBX
    ret


# Exit_Failure function
# Receives nothing
# And aborts hard
# Does NOT return
:Exit_Failure
    mov_ebx, %1                                 # All is wrong
    mov_eax, %1                                 # put the exit syscall number in eax
    int !0x80                                   # Call it a bad day

# Keywords
:union  "union"
:struct  "struct"
:constant  "CONSTANT"
:main_string  "main"
:argc_string  "argc"
:argv_string  "argv"
:if_string  "if"
:else_string  "else"
:do_string  "do"
:while_string  "while"
:for_string  "for"
:asm_string  "asm"
:goto_string  "goto"
:return_string  "return"
:break_string  "break"
:continue_string  "continue"
:sizeof_string  "sizeof"
:plus_string  "+"
:minus_string  "-"
:multiply_string  "*"
:divide_string  "/"
:modulus_string  "%"
:left_shift_string  "<<"
:right_shift_string  ">>"
:less_than_string  "<"
:less_than_equal_string  "<="
:greater_than_equal_string  ">="
:greater_than_string  ">"
:equal_to_string  "=="
:not_equal_string  "!="
:bitwise_and  "&"
:logical_and  "&&"
:bitwise_or  "|"
:logical_or  "||"
:bitwise_xor  "^"
:arrow_string  "->"


# Frequently Used strings
# Generally used by require_match
:open_curly_brace  "{"
:close_curly_brace  "}"
:open_paren  "("
:close_paren  ")"
:open_bracket  "["
:close_bracket  "]"
:comma  ","
:semicolon  ";"
:equal  "="
:percent  "%"
:newline  "\n"
:underline  "_"


:prim_types
:type_void
    &type_int                                   # NEXT
    %4                                          # SIZE
    %0                                          # OFFSET
    &type_void                                  # INDIRECT
    %0                                          # MEMBERS
    &type_void                                  # TYPE
    &type_void_name                             # NAME
:type_void_name  "void"

:type_int
    &type_char                                  # NEXT
    %4                                          # SIZE
    %0                                          # OFFSET
    &type_int                                   # INDIRECT
    %0                                          # MEMBERS
    &type_int                                   # TYPE
    &type_int_name                              # NAME
:type_int_name  "int"

:type_char
    &type_file                                  # NEXT
    %1                                          # SIZE
    %0                                          # OFFSET
    &type_char_indirect                         # INDIRECT
    %0                                          # MEMBERS
    &type_char                                  # TYPE
    &type_char_name                             # NAME
:type_char_name  "char"

:type_char_indirect
    &type_file                                  # NEXT
    %4                                          # SIZE
    %0                                          # OFFSET
    &type_char_double_indirect                  # INDIRECT
    %0                                          # MEMBERS
    &type_char_indirect                         # TYPE
    &type_char_indirect_name                    # NAME
:type_char_indirect_name  "char*"

:type_char_double_indirect
    &type_file                                  # NEXT
    %4                                          # SIZE
    %0                                          # OFFSET
    &type_char_double_indirect                  # INDIRECT
    %0                                          # MEMBERS
    &type_char_indirect                         # TYPE
    &type_char_double_indirect_name             # NAME
:type_char_double_indirect_name  "char**"

:type_file
    &type_function                              # NEXT
    %4                                          # SIZE
    %0                                          # OFFSET
    &type_file                                  # INDIRECT
    %0                                          # MEMBERS
    &type_file                                  # TYPE
    &type_file_name                             # NAME
:type_file_name  "FILE"

:type_function
    &type_unsigned                              # NEXT
    %4                                          # SIZE
    %0                                          # OFFSET
    &type_function                              # INDIRECT
    %0                                          # MEMBERS
    &type_function                              # TYPE
    &type_function_name                         # NAME
:type_function_name  "FUNCTION"

:type_unsigned
    &type_long                                  # NEXT
    %4                                          # SIZE
    %0                                          # OFFSET
    &type_unsigned                              # INDIRECT
    %0                                          # MEMBERS
    &type_unsigned                              # TYPE
    &type_unsigned_name                         # NAME
:type_unsigned_name  "unsigned"

:type_long
    %0                                          # NEXT
    %4                                          # SIZE
    %0                                          # OFFSET
    &type_long                                  # INDIRECT
    %0                                          # MEMBERS
    &type_long                                  # TYPE
    &type_long_name                             # NAME
:type_long_name  "long"

# debug_list function
# Receives struct token_list* in EAX
# Prints contents of list and exits
# Does NOT return
:debug_list
    mov_ebp,eax                                 # Protect the list pointer
    mov_eax, %2                                 # Using Standard error
    mov_[DWORD],eax &Output_file                # write to standard error

:debug_list_iter
    # Header
    mov_eax, &debug_list_string0                # Using our first string
    call %File_Print                            # Print it
    mov_eax,ebp                                 # Use address of pointer
    call %numerate_number                       # Convert it into string
    call %File_Print                            # Print it

    # NEXT
    mov_eax, &debug_list_string1                # Using our second string
    call %File_Print                            # Print it
    mov_eax,[ebp+BYTE] !0                       # Use address of pointer
    call %numerate_number                       # Convert it into string
    call %File_Print                            # Print it

    # PREV
    mov_eax, &debug_list_string2                # Using our third string
    call %File_Print                            # Print it
    mov_eax,[ebp+BYTE] !4                       # Use address of pointer
    call %numerate_number                       # Convert it into string
    call %File_Print                            # Print it

    # S
    mov_eax, &debug_list_string3                # Using our fourth string
    call %File_Print                            # Print it
    mov_eax,[ebp+BYTE] !8                       # Use address of pointer
    call %numerate_number                       # Convert it into string
    call %File_Print                            # Print it

    # S Contents
    mov_eax, &debug_list_string4                # Using our fifth string
    call %File_Print                            # Print it
    mov_eax,[ebp+BYTE] !8                       # Use address of string
    cmp_eax, !0                                 # IF NULL Pointer
    jne %debug_list_null                        # otherwise display
    mov_eax, &debug_list_string_null            # Give meaningful message instead
:debug_list_null
    call %File_Print                            # Print it

    # TYPE
    mov_eax, &debug_list_string5                # Using our sixth string
    call %File_Print                            # Print it
    mov_eax,[ebp+BYTE] !12                      # Use address of pointer
    call %numerate_number                       # Convert it into string
    call %File_Print                            # Print it

    # ARGS/DEPTH
    mov_eax, &debug_list_string6                # Using our seventh string
    call %File_Print                            # Print it
    mov_eax,[ebp+BYTE] !16                      # Use address of pointer
    call %numerate_number                       # Convert it into string
    call %File_Print                            # Print it

    mov_eax, %10                                # Add "\n"
    call %fputc                                 # print it
    call %fputc                                 # print it

    mov_ebp,[ebp]                               # TOKEN = TOKEN->NEXT
    cmp_ebp, !0                                 # Check if NULL
    jne %debug_list_iter                        # iterate otherwise

    mov_ebx, %666                               # All is HELL
    mov_eax, %1                                 # put the exit syscall number in eax
    int !0x80                                   # Call it a bad day

:debug_list_string0  "Token_list node at address: "
:debug_list_string1  "
NEXT address: "
:debug_list_string2  "
PREV address: "
:debug_list_string3  "
S address: "
:debug_list_string4  "
The contents of S are: "
:debug_list_string5  "
TYPE address: "
:debug_list_string6  "
ARGUMENTS address: "
:debug_list_string_null  ">::<NULL>::<"


:Address_of
    NULL
:C
    NULL
:Input_file
    NULL
:MALLOC
    NULL
:Output_file
    NULL
:Token
    NULL
:break_frame
    NULL
:break_target_func
    NULL
:break_target_head
    NULL
:break_target_num
    NULL
:current_count
    NULL
:current_target
    NULL
:function
    NULL
:global_constant_list
    NULL
:global_function_list
    NULL
:global_symbol_list
    NULL
:global_token
    NULL
:global_types
    &prim_types
:globals_list
    NULL
:output_list
    NULL
:string_index
    NULL
:strings_list
    NULL

:ELF_end

File /M2libc/x86/linux/bootstrap.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/x86/linux/bootstrap.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/x86/linux/bootstrap.c
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

// CONSTANT stdin 0
// CONSTANT stdout 1
// CONSTANT stderr 2
// CONSTANT EOF 0xFFFFFFFF
// CONSTANT NULL 0
// CONSTANT EXIT_FAILURE 1
// CONSTANT EXIT_SUCCESS 0
// CONSTANT TRUE 1
// CONSTANT FALSE 0


int fgetc(FILE* f)
{
    asm("mov_eax, %3"
        "lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "push_ebx"
        "mov_ecx,esp"
        "mov_edx, %1"
        "int !0x80"
        "test_eax,eax"
        "pop_eax"
        "jne %FUNCTION_fgetc_Done"
        "mov_eax, %-1"
        ":FUNCTION_fgetc_Done");
}

void fputc(char s, FILE* f)
{
    asm("mov_eax, %4"
        "lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_edx, %1"
        "int !0x80");
}

void fputs(char* s, FILE* f)
{
    while(0 != s[0])
    {
        fputc(s[0], f);
        s = s + 1;
    }
}

FILE* open(char* name, int flag, int mode)
{
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %5"
        "int !0x80");
}

FILE* fopen(char* filename, char* mode)
{
    FILE* f;
    if('w' == mode[0])
    { /* 577 is O_WRONLY|O_CREAT|O_TRUNC, 384 is 600 in octal */
        f = open(filename, 577 , 384);
    }
    else
    { /* Everything else is a read */
        f = open(filename, 0, 0);
    }

    /* Negative numbers are error codes */
    if(0 > f)
    {
        return 0;
    }
    return f;
}

int close(int fd)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %6"
        "int !0x80");
}

int fclose(FILE* stream)
{
    int error = close(stream);
    return error;
}

int brk(void *addr)
{
    asm("mov_eax,[esp+DWORD] %4"
        "push_eax"
        "mov_eax, %45"
        "pop_ebx"
        "int !0x80");
}

long _malloc_ptr;
long _brk_ptr;

void* malloc(int size)
{
    if(NULL == _brk_ptr)
    {
        _brk_ptr = brk(0);
        _malloc_ptr = _brk_ptr;
    }

    if(_brk_ptr < _malloc_ptr + size)
    {
        _brk_ptr = brk(_malloc_ptr + size);
        if(-1 == _brk_ptr) return 0;
    }

    long old_malloc = _malloc_ptr;
    _malloc_ptr = _malloc_ptr + size;
    return old_malloc;
}

int strlen(char* str )
{
    int i = 0;
    while(0 != str[i]) i = i + 1;
    return i;
}

void* memset(void* ptr, int value, int num)
{
    char* s;
    for(s = ptr; 0 < num; num = num - 1)
    {
        s[0] = value;
        s = s + 1;
    }
}

void* calloc(int count, int size)
{
    void* ret = malloc(count * size);
    if(NULL == ret) return NULL;
    memset(ret, 0, (count * size));
    return ret;
}

void free(void* l)
{
    return;
}

void exit(int value)
{
    asm("pop_ebx"
        "pop_ebx"
        "mov_eax, %1"
        "int !0x80");
}

File /M2-Planet/cc.h

Live-bootstrap source file is 'seed/stage0-posix/M2-Planet/cc.h'.
URL: https://github.com/oriansj/M2-Planet/blob/0479606ed5131a64cd1dd2dd3f257e73cd9dce68/cc.h
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// CONSTANT FALSE 0
#define FALSE 0
// CONSTANT TRUE 1
#define TRUE 1

// CONSTANT KNIGHT_NATIVE 1
#define KNIGHT_NATIVE 1
// CONSTANT KNIGHT_POSIX 2
#define KNIGHT_POSIX 2
// CONSTANT X86 3
#define X86 3
// CONSTANT AMD64 4
#define AMD64 4
// CONSTANT ARMV7L 5
#define ARMV7L 5
// CONSTANT AARCH64 6
#define AARCH64 6
// CONSTANT RISCV32 7
#define RISCV32 7
// CONSTANT RISCV64 8
#define RISCV64 8


void copy_string(char* target, char* source, int max);
int in_set(int c, char* s);
int match(char* a, char* b);
void require(int bool, char* error);
void reset_hold_string();


struct type
{
    struct type* next;
    int size;
    int offset;
    int is_signed;
    struct type* indirect;
    struct type* members;
    struct type* type;
    char* name;
};

struct token_list
{
    struct token_list* next;
    union
    {
        struct token_list* locals;
        struct token_list* prev;
    };
    char* s;
    union
    {
        struct type* type;
        char* filename;
    };
    union
    {
        struct token_list* arguments;
        int depth;
        int linenumber;
    };
};

struct case_list
{
    struct case_list* next;
    char* value;
};

#include "cc_globals.h"

File /M2libc/bootstrappable.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/bootstrappable.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/bootstrappable.c
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>


#define TRUE 1
#define FALSE 0


void require(int bool, char* error)
{
    if(!bool)
    {
        fputs(error, stderr);
        exit(EXIT_FAILURE);
    }
}


int match(char* a, char* b)
{
    if((NULL == a) && (NULL == b)) return TRUE;
    if(NULL == a) return FALSE;
    if(NULL == b) return FALSE;

    int i = -1;
    do
    {
        i = i + 1;
        if(a[i] != b[i])
        {
            return FALSE;
        }
    } while((0 != a[i]) && (0 !=b[i]));
    return TRUE;
}


int in_set(int c, char* s)
{
    /* NULL set is always false */
    if(NULL == s) return FALSE;

    while(0 != s[0])
    {
        if(c == s[0]) return TRUE;
        s = s + 1;
    }
    return FALSE;
}

/* INTERNAL ONLY */
int __index_number(char* s, char c)
{
    int i = 0;
    while(s[i] != c)
    {
        i = i + 1;
        if(0 == s[i]) return -1;
    }
    return i;
}

/* INTERNAL ONLY */
int __toupper(int c)
{
    if(in_set(c, "abcdefghijklmnopqrstuvwxyz")) return (c & 0xDF);
    return c;
}

/* INTERNAL ONLY */
int __set_reader(char* set, int mult, char* input)
{
    int n = 0;
    int i = 0;
    int hold;
    int negative_p = FALSE;

    if(input[0] == '-')
    {
        negative_p = TRUE;
        i = i + 1;
    }

    while(in_set(input[i], set))
    {
        n = n * mult;
        hold = __index_number(set, __toupper(input[i]));

        /* Input managed to change between in_set and index_number */
        if(-1 == hold) return 0;
        n = n + hold;
        i = i + 1;
    }

    /* loop exited before NULL and thus invalid input */
    if(0 != input[i]) return 0;

    if(negative_p)
    {
        n = 0 - n;
    }

    return n;
}

int strtoint(char *a)
{
    int result = 0;
    /* If NULL string */
    if(0 == a[0])
    {
        result = 0;
    }
    /* Deal with binary */
    else if ('0' == a[0] && 'b' == a[1])
    {
        result = __set_reader("01", 2, a+2);
    }
    /* Deal with hex */
    else if ('0' == a[0] &&  'x' == a[1])
    {
        result = __set_reader("0123456789ABCDEFabcdef", 16, a+2);
    }
    /* Deal with octal */
    else if('0' == a[0])
    {
        result = __set_reader("01234567", 8, a+1);
    }
    /* Deal with decimal */
    else
    {
        result = __set_reader("0123456789", 10, a);
    }

    /* Deal with sign extension for 64bit hosts */
    if(0 != (0x80000000 & result)) result = (0xFFFFFFFF << 31) | result;
    return result;
}

char* int2str(int x, int base, int signed_p)
{
    require(1 < base, "int2str doesn't support a base less than 2\n");
    require(37 > base, "int2str doesn't support a base more than 36\n");
    /* Be overly conservative and save space for 32binary digits and padding null */
    char* p = calloc(34, sizeof(char));
    /* if calloc fails return null to let calling code deal with it */
    if(NULL == p) return p;

    p = p + 32;
    unsigned i;
    int sign_p = FALSE;
    char* table = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    if(signed_p && (10 == base) && (0 != (x & 0x80000000)))
    {
        /* Truncate to 31bits */
        i = -x & 0x7FFFFFFF;
        if(0 == i) return "-2147483648";
        sign_p = TRUE;
    } /* Truncate to 32bits */
    else i = x & (0x7FFFFFFF | (1 << 31));

    do
    {
        p[0] = table[i % base];
        p = p - 1;
        i = i / base;
    } while(0 < i);

    if(sign_p)
    {
        p[0] = '-';
        p = p - 1;
    }

    return p + 1;
}

File /M2-Planet/cc_globals.c

Live-bootstrap source file is 'seed/stage0-posix/M2-Planet/cc_globals.c'.
URL: https://github.com/oriansj/M2-Planet/blob/0479606ed5131a64cd1dd2dd3f257e73cd9dce68/cc_globals.c
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

/* What types we have */
struct type* global_types;
struct type* prim_types;

/* What we are currently working on */
struct token_list* global_token;

/* Output reorder collections*/
struct token_list* output_list;
struct token_list* strings_list;
struct token_list* globals_list;

/* Make our string collection more efficient */
char* hold_string;
int string_index;

/* Our Target Architecture */
int Architecture;
int register_size;

int MAX_STRING;
struct type* integer;

/* enable bootstrap-mode */
int BOOTSTRAP_MODE;

/* enable preprocessor-only mode */
int PREPROCESSOR_MODE;

File /M2-Planet/cc_reader.c

Live-bootstrap source file is 'seed/stage0-posix/M2-Planet/cc_reader.c'.
URL: https://github.com/oriansj/M2-Planet/blob/0479606ed5131a64cd1dd2dd3f257e73cd9dce68/cc_reader.c
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "cc.h"

int strtoint(char *a);

/* Globals */
FILE* input;
struct token_list* token;
int line;
char* file;

int grab_byte()
{
    int c = fgetc(input);
    if(10 == c) line = line + 1;
    return c;
}

int clearWhiteSpace(int c)
{
    if((32 == c) || (9 == c)) return clearWhiteSpace(grab_byte());
    return c;
}

int consume_byte(int c)
{
    hold_string[string_index] = c;
    string_index = string_index + 1;
    require(MAX_STRING > string_index, "Token exceeded MAX_STRING char limit\nuse --max-string number to increase\n");
    return grab_byte();
}

int preserve_string(int c)
{
    int frequent = c;
    int escape = FALSE;
    do
    {
        if(!escape && '\\' == c ) escape = TRUE;
        else escape = FALSE;
        c = consume_byte(c);
        require(EOF != c, "Unterminated string\n");
    } while(escape || (c != frequent));
    return grab_byte();
}


void copy_string(char* target, char* source, int max)
{
    int i = 0;
    while(0 != source[i])
    {
        target[i] = source[i];
        i = i + 1;
        if(i == max) break;
    }
}


void fixup_label()
{
    int hold = ':';
    int prev;
    int i = 0;
    do
    {
        prev = hold;
        hold = hold_string[i];
        hold_string[i] = prev;
        i = i + 1;
    } while(0 != hold);
}

int preserve_keyword(int c, char* S)
{
    while(in_set(c, S))
    {
        c = consume_byte(c);
    }
    return c;
}

void reset_hold_string()
{
    int i = MAX_STRING;
    while(0 <= i)
    {
        hold_string[i] = 0;
        i = i - 1;
    }
    string_index = 0;
}

/* note if this is the first token in the list, head needs fixing up */
struct token_list* eat_token(struct token_list* token)
{
    if(NULL != token->prev)
    {
        token->prev->next = token->next;
    }

    /* update backlinks */
    if(NULL != token->next)
    {
        token->next->prev = token->prev;
    }

    return token->next;
}

struct token_list* eat_until_newline(struct token_list* head)
{
    while (NULL != head)
    {
        if('\n' == head->s[0])
        {
            return head;
        }
        else
        {
            head = eat_token(head);
        }
    }

    return NULL;
}

struct token_list* remove_line_comments(struct token_list* head)
{
    struct token_list* first = NULL;

    while (NULL != head)
    {
        if(match("//", head->s))
        {
            head = eat_until_newline(head);
        }
        else
        {
            if(NULL == first)
            {
                first = head;
            }
            head = head->next;
        }
    }

    return first;
}

struct token_list* remove_line_comment_tokens(struct token_list* head)
{
    struct token_list* first = NULL;

    while (NULL != head)
    {
        if(match("//", head->s))
        {
            head = eat_token(head);
        }
        else
        {
            if(NULL == first)
            {
                first = head;
            }
            head = head->next;
        }
    }

    return first;
}

struct token_list* remove_preprocessor_directives(struct token_list* head)
{
    struct token_list* first = NULL;

    while (NULL != head)
    {
        if('#' == head->s[0])
        {
            head = eat_until_newline(head);
        }
        else
        {
            if(NULL == first)
            {
                first = head;
            }
            head = head->next;
        }
    }

    return first;
}

void new_token(char* s, int size)
{
    struct token_list* current = calloc(1, sizeof(struct token_list));
    require(NULL != current, "Exhausted memory while getting token\n");

    /* More efficiently allocate memory for string */
    current->s = calloc(size, sizeof(char));
    require(NULL != current->s, "Exhausted memory while trying to copy a token\n");
    copy_string(current->s, s, MAX_STRING);

    current->prev = token;
    current->next = token;
    current->linenumber = line;
    current->filename = file;
    token = current;
}

int get_token(int c)
{
    struct token_list* current = calloc(1, sizeof(struct token_list));
    require(NULL != current, "Exhausted memory while getting token\n");

reset:
    reset_hold_string();
    string_index = 0;

    c = clearWhiteSpace(c);
    if(c == EOF)
    {
        free(current);
        return c;
    }
    else if('#' == c)
    {
        c = consume_byte(c);
        c = preserve_keyword(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
    }
    else if(in_set(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"))
    {
        c = preserve_keyword(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
        if(':' == c)
        {
            fixup_label();
            c = ' ';
        }
    }
    else if(in_set(c, "<=>|&!^%"))
    {
        c = preserve_keyword(c, "<=>|&!^%");
    }
    else if(in_set(c, "'\""))
    {
        c = preserve_string(c);
    }
    else if(c == '/')
    {
        c = consume_byte(c);
        if(c == '*')
        {
            c = grab_byte();
            while(c != '/')
            {
                while(c != '*')
                {
                    c = grab_byte();
                    require(EOF != c, "Hit EOF inside of block comment\n");
                }
                c = grab_byte();
                require(EOF != c, "Hit EOF inside of block comment\n");
            }
            c = grab_byte();
            goto reset;
        }
        else if(c == '/')
        {
            c = consume_byte(c);
        }
        else if(c == '=')
        {
            c = consume_byte(c);
        }
    }
    else if (c == '\n')
    {
        c = consume_byte(c);
    }
    else if(c == '*')
    {
        c = consume_byte(c);
        if(c == '=')
        {
            c = consume_byte(c);
        }
    }
    else if(c == '+')
    {
        c = consume_byte(c);
        if(c == '=')
        {
            c = consume_byte(c);
        }
        if(c == '+')
        {
            c = consume_byte(c);
        }
    }
    else if(c == '-')
    {
        c = consume_byte(c);
        if(c == '=')
        {
            c = consume_byte(c);
        }
        if(c == '>')
        {
            c = consume_byte(c);
        }
        if(c == '-')
        {
            c = consume_byte(c);
        }
    }
    else
    {
        c = consume_byte(c);
    }

    new_token(hold_string, string_index + 2);
    return c;
}


int consume_filename(int c)
{
    reset_hold_string();
    int done = FALSE;

    while(!done)
    {
        if(c == EOF)
        {
            fputs("we don't support EOF as a filename in #FILENAME statements\n", stderr);
            exit(EXIT_FAILURE);
        }
        else if((32 == c) || (9 == c) || (c == '\n'))
        {
            c = grab_byte();
        }
        else
        {
            do
            {
                c = consume_byte(c);
                require(EOF != c, "Unterminated filename in #FILENAME\n");
            } while((32 != c) && (9 != c) && ('\n' != c));
            done = TRUE;
        }
    }

    /* with just a little extra to put in the matching at the end */
    new_token(hold_string, string_index + 3);
    return c;
}


int change_filename(int ch)
{
    require(EOF != ch, "#FILENAME failed to receive filename\n");
    /* Remove the #FILENAME */
    token = token->next;

    /* Get new filename */
    ch = consume_filename(ch);
    file = token->s;
    /* Remove it from the processing list */
    token = token->next;
    require(EOF != ch, "#FILENAME failed to receive filename\n");

    /* Get new line number */
    ch = get_token(ch);
    line = strtoint(token->s);
    if(0 == line)
    {
        if('0' != token->s[0])
        {
            fputs("non-line number: ", stderr);
            fputs(token->s, stderr);
            fputs(" provided to #FILENAME\n", stderr);
            exit(EXIT_FAILURE);
        }
    }
    /* Remove it from the processing list */
    token = token->next;

    return ch;
}

struct token_list* reverse_list(struct token_list* head)
{
    struct token_list* root = NULL;
    struct token_list* next;
    while(NULL != head)
    {
        next = head->next;
        head->next = root;
        root = head;
        head = next;
    }
    return root;
}

struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename)
{
    input  = a;
    line = 1;
    file = filename;
    token = current;
    int ch = grab_byte();
    while(EOF != ch)
    {
        ch = get_token(ch);
        require(NULL != token, "Empty files don't need to be compiled\n");
        if(match("#FILENAME", token->s)) ch = change_filename(ch);
    }

    return token;
}

File /M2-Planet/cc_strings.c

Live-bootstrap source file is 'seed/stage0-posix/M2-Planet/cc_strings.c'.
URL: https://github.com/oriansj/M2-Planet/blob/0479606ed5131a64cd1dd2dd3f257e73cd9dce68/cc_strings.c
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "cc.h"
#include <stdint.h>

struct token_list* emit(char *s, struct token_list* head);
void require(int bool, char* error);

char upcase(char a)
{
    if(in_set(a, "abcdefghijklmnopqrstuvwxyz"))
    {
        a = a - 32;
    }

    return a;
}


int char2hex(int c)
{
    if (c >= '0' && c <= '9') return (c - 48);
    else if (c >= 'a' && c <= 'f') return (c - 87);
    else if (c >= 'A' && c <= 'F') return (c - 55);
    else return -1;
}

int hexify(int c, int high)
{
    int i = char2hex(c);

    if(0 > i)
    {
        fputs("Tried to print non-hex number\n", stderr);
        exit(EXIT_FAILURE);
    }

    if(high)
    {
        i = i << 4;
    }
    return i;
}

int escape_lookup(char* c);
int weird(char* string)
{
    int c;
    string = string + 1;
weird_reset:
    c = string[0];
    if(0 == c) return FALSE;
    if('\\' == c)
    {
        c = escape_lookup(string);
        if('x' == string[1]) string = string + 2;
        string = string + 1;
    }

    if(!in_set(c, "\t\n !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")) return TRUE;
    if(in_set(c, " \t\n\r") && (':' == string[1])) return TRUE;
    string = string + 1;
    goto weird_reset;
}

/* Lookup escape values */
int escape_lookup(char* c)
{
    if('\\' != c[0]) return c[0];

    if(c[1] == 'x')
    {
        int t1 = hexify(c[2], TRUE);
        int t2 = hexify(c[3], FALSE);
        return t1 + t2;
    }
    else if(c[1] == '0') return 0;
    else if(c[1] == 'a') return 7;
    else if(c[1] == 'b') return 8;
    else if(c[1] == 't') return 9;
    else if(c[1] == 'n') return 10;
    else if(c[1] == 'v') return 11;
    else if(c[1] == 'f') return 12;
    else if(c[1] == 'r') return 13;
    else if(c[1] == 'e') return 27;
    else if(c[1] == '"') return 34;
    else if(c[1] == '\'') return 39;
    else if(c[1] == '\\') return 92;

    fputs("Unknown escape received: ", stderr);
    fputs(c, stderr);
    fputs(" Unable to process\n", stderr);
    exit(EXIT_FAILURE);
}

/* Deal with human strings */
char* collect_regular_string(char* string)
{
    string_index = 0;

collect_regular_string_reset:
    require((MAX_STRING - 3) > string_index, "Attempt at parsing regular string exceeds max length\n");
    if(string[0] == '\\')
    {
        hold_string[string_index] = escape_lookup(string);
        if (string[1] == 'x') string = string + 2;
        string = string + 2;
    }
    else
    {
        hold_string[string_index] = string[0];
        string = string + 1;
    }

    string_index = string_index + 1;
    if(string[0] != 0) goto collect_regular_string_reset;

    hold_string[string_index] = '"';
    hold_string[string_index + 1] = '\n';
    char* message = calloc(string_index + 3, sizeof(char));
    require(NULL != message, "Exhausted memory while storing regular string\n");
    copy_string(message, hold_string, string_index + 2);
    reset_hold_string();
    return message;
}

/* Deal with non-human strings */
char* collect_weird_string(char* string)
{
    string_index = 1;
    int temp;
    char* table = "0123456789ABCDEF";

    hold_string[0] = '\'';
collect_weird_string_reset:
    require((MAX_STRING - 6) > string_index, "Attempt at parsing weird string exceeds max length\n");
    string = string + 1;
    hold_string[string_index] = ' ';
    temp = escape_lookup(string) & 0xFF;
    hold_string[string_index + 1] = table[(temp >> 4)];
    hold_string[string_index + 2] = table[(temp & 15)];

    if(string[0] == '\\')
    {
        if(string[1] == 'x') string = string + 2;
        string = string + 1;
    }

    string_index = string_index + 3;
    if(string[1] != 0) goto collect_weird_string_reset;

    hold_string[string_index] = ' ';
    hold_string[string_index + 1] = '0';
    hold_string[string_index + 2] = '0';
    hold_string[string_index + 3] = '\'';
    hold_string[string_index + 4] = '\n';

    char* hold = calloc(string_index + 6, sizeof(char));
    require(NULL != hold, "Exhausted available memory while attempting to collect a weird string\n");
    copy_string(hold, hold_string, string_index + 5);
    reset_hold_string();
    return hold;
}

/* Parse string to deal with hex characters*/
char* parse_string(char* string)
{
    /* the string */
    if(weird(string)) return collect_weird_string(string);
    else return collect_regular_string(string);
}

File /M2-Planet/cc_types.c

Live-bootstrap source file is 'seed/stage0-posix/M2-Planet/cc_types.c'.
URL: https://github.com/oriansj/M2-Planet/blob/0479606ed5131a64cd1dd2dd3f257e73cd9dce68/cc_types.c
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "cc.h"

/* Imported functions */
int strtoint(char *a);
void line_error();
void require(int bool, char* error);

/* enable easy primitive extension */
struct type* add_primitive(struct type* a)
{
    if(NULL == prim_types) return a;
    struct type* i = prim_types;
    while(NULL != i->next)
    {
        i = i->next;
    }
    i->next = a;

    return prim_types;
}

/* enable easy primitive creation */
struct type* new_primitive(char* name0, char* name1, char* name2, int size, int sign)
{
    /* Create type** */
    struct type* a = calloc(1, sizeof(struct type));
    require(NULL != a, "Exhausted memory while declaring new primitive**\n");
    a->name = name2;
    a->size = register_size;
    a->indirect = a;
    a->is_signed = sign;

    /* Create type* */
    struct type* b = calloc(1, sizeof(struct type));
    require(NULL != b, "Exhausted memory while declaring new primitive*\n");
    b->name = name1;
    b->size = register_size;
    b->is_signed = sign;
    b->indirect = a;
    a->type = b;

    struct type* r = calloc(1, sizeof(struct type));
    require(NULL != r, "Exhausted memory while declaring new primitive\n");
    r->name = name0;
    r->size = size;
    r->is_signed = sign;
    r->indirect = b;
    r->type = r;
    b->type = r;

    return r;
}

/* Initialize default types */
void initialize_types()
{
    if(AMD64 == Architecture || AARCH64 == Architecture || RISCV64 == Architecture) register_size = 8;
    else register_size = 4;

    /* Define void */
    struct type* hold = new_primitive("void", "void*", "void**", register_size, FALSE);
    prim_types = add_primitive(hold);

    /* Define unsigned LONG */
    hold = new_primitive("SCM","SCM*", "SCM**", register_size, FALSE);
    prim_types = add_primitive(hold);

    /* Define LONG */
    hold = new_primitive("long", "long*", "long**", register_size, TRUE);
    prim_types = add_primitive(hold);

    /* Define UNSIGNED */
    hold = new_primitive("unsigned", "unsigned*", "unsigned**", register_size, FALSE);
    prim_types = add_primitive(hold);

    /* Define int */
    integer = new_primitive("int", "int*", "int**", register_size, TRUE);
    prim_types = add_primitive(integer);

    /* Define uint32_t */
    hold = new_primitive("uint32_t", "uint32_t*", "uint32_t**", 4, FALSE);
    prim_types = add_primitive(hold);

    /* Define int32_t */
    hold = new_primitive("int32_t", "int32_t*", "int32_t**", 4, TRUE);
    prim_types = add_primitive(hold);

    /* Define uint16_t */
    hold = new_primitive("uint16_t", "uint16_t*", "uint16_t**", 2, FALSE);
    prim_types = add_primitive(hold);

    /* Define int16_t */
    hold = new_primitive("int16_t", "int16_t*", "int16_t**", 2, TRUE);
    prim_types = add_primitive(hold);

    /* Define uint8_t */
    hold = new_primitive("uint8_t", "uint8_t*", "uint8_t**", 1, FALSE);
    prim_types = add_primitive(hold);

    /* Define int8_t */
    hold = new_primitive("int8_t", "int8_t*", "int8_t**", 1, TRUE);
    prim_types = add_primitive(hold);

    /* Define char */
    hold = new_primitive("char", "char*", "char**", 1, TRUE);
    prim_types = add_primitive(hold);

    /* Define FUNCTION */
    hold = new_primitive("FUNCTION", "FUNCTION*", "FUNCTION**", register_size, FALSE);
    prim_types = add_primitive(hold);

    if(BOOTSTRAP_MODE)
    {
        /* Define FILE */
        hold = new_primitive("FILE", "FILE*", "FILE**", register_size, TRUE);
        prim_types = add_primitive(hold);

        /* Primitives mes.c wanted */
        hold = new_primitive("size_t", "size_t*", "size_t**", register_size, FALSE);
        prim_types = add_primitive(hold);

        hold = new_primitive("ssize_t", "ssize_t*", "ssize_t**", register_size, FALSE);
        prim_types = add_primitive(hold);
    }

    global_types = prim_types;
}

struct type* lookup_type(char* s, struct type* start)
{
    struct type* i;
    for(i = start; NULL != i; i = i->next)
    {
        if(match(i->name, s))
        {
            return i;
        }
    }
    return NULL;
}

struct type* lookup_member(struct type* parent, char* name)
{
    struct type* i;
    require(NULL != parent, "Not a valid struct type\n");
    for(i = parent->members; NULL != i; i = i->members)
    {
        if(match(i->name, name)) return i;
    }

    fputs("ERROR in lookup_member ", stderr);
    fputs(parent->name, stderr);
    fputs("->", stderr);
    fputs(global_token->s, stderr);
    fputs(" does not exist\n", stderr);
    line_error();
    fputs("\n", stderr);
    exit(EXIT_FAILURE);
}

struct type* type_name();
void require_match(char* message, char* required);

int member_size;
struct type* build_member(struct type* last, int offset)
{
    struct type* i = calloc(1, sizeof(struct type));
    require(NULL != i, "Exhausted memory while building a struct member\n");
    i->members = last;
    i->offset = offset;

    struct type* member_type = type_name();
    require(NULL != member_type, "struct member type can not be invalid\n");
    i->type = member_type;
    i->name = global_token->s;
    global_token = global_token->next;
    require(NULL != global_token, "struct member can not be EOF terminated\n");

    /* Check to see if array */
    if(match( "[", global_token->s))
    {
        global_token = global_token->next;
        require(NULL != global_token, "struct member arrays can not be EOF sized\n");
        i->size = member_type->type->size * strtoint(global_token->s);
        if(0 == i->size)
        {
            fputs("Struct only supports [num] form\n", stderr);
            exit(EXIT_FAILURE);
        }
        global_token = global_token->next;
        require_match("Struct only supports [num] form\n", "]");
    }
    else
    {
        i->size = member_type->size;
    }
    member_size = i->size;

    return i;
}

struct type* build_union(struct type* last, int offset)
{
    int size = 0;
    global_token = global_token->next;
    require_match("ERROR in build_union\nMissing {\n", "{");
    while('}' != global_token->s[0])
    {
        last = build_member(last, offset);
        if(member_size > size)
        {
            size = member_size;
        }
        require_match("ERROR in build_union\nMissing ;\n", ";");
        require(NULL != global_token, "Unterminated union\n");
    }
    member_size = size;
    global_token = global_token->next;
    return last;
}

void create_struct()
{
    int offset = 0;
    member_size = 0;
    struct type* head = calloc(1, sizeof(struct type));
    require(NULL != head, "Exhausted memory while creating a struct\n");
    struct type* i = calloc(1, sizeof(struct type));
    require(NULL != i, "Exhausted memory while creating a struct indirection\n");
    struct type* ii = calloc(1, sizeof(struct type));
    require(NULL != ii, "Exhausted memory while creating a struct double indirection\n");
    head->name = global_token->s;
    head->type = head;
    head->indirect = i;
    head->next = global_types;
    i->name = global_token->s;
    i->type = head;
    i->indirect = ii;
    i->size = register_size;
    ii->name = global_token->s;
    ii->type = i;
    ii->indirect = ii;
    ii->size = register_size;
    global_types = head;
    global_token = global_token->next;
    require_match("ERROR in create_struct\n Missing {\n", "{");
    struct type* last = NULL;
    require(NULL != global_token, "Incomplete struct definition at end of file\n");
    while('}' != global_token->s[0])
    {
        if(match(global_token->s, "union"))
        {
            last = build_union(last, offset);
        }
        else
        {
            last = build_member(last, offset);
        }
        offset = offset + member_size;
        require_match("ERROR in create_struct\n Missing ;\n", ";");
        require(NULL != global_token, "Unterminated struct\n");
    }

    global_token = global_token->next;
    require_match("ERROR in create_struct\n Missing ;\n", ";");

    head->size = offset;
    head->members = last;
    i->members = last;
}


struct type* type_name()
{
    struct type* ret;

    require(NULL != global_token, "Received EOF instead of type name\n");

    if(match("extern", global_token->s))
    {
        global_token = global_token->next;
        require(NULL != global_token, "unfinished type definition in extern\n");
    }

    if(match("struct", global_token->s))
    {
        global_token = global_token->next;
        require(NULL != global_token, "structs can not have a EOF type name\n");
        ret = lookup_type(global_token->s, global_types);
        if(NULL == ret)
        {
            create_struct();
            return NULL;
        }
    }
    else
    {
        ret = lookup_type(global_token->s, global_types);
        if(NULL == ret)
        {
            fputs("Unknown type ", stderr);
            fputs(global_token->s, stderr);
            fputs("\n", stderr);
            line_error();
            fputs("\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    global_token = global_token->next;
    require(NULL != global_token, "unfinished type definition\n");

    if(match("const", global_token->s))
    {
        global_token = global_token->next;
        require(NULL != global_token, "unfinished type definition in const\n");
    }

    while(global_token->s[0] == '*')
    {
        ret = ret->indirect;
        global_token = global_token->next;
        require(NULL != global_token, "unfinished type definition in indirection\n");
    }

    return ret;
}

struct type* mirror_type(struct type* source, char* name)
{
    struct type* head = calloc(1, sizeof(struct type));
    require(NULL != head, "Exhausted memory while creating a struct\n");
    struct type* i = calloc(1, sizeof(struct type));
    require(NULL != i, "Exhausted memory while creating a struct indirection\n");

    head->name = name;
    i->name = name;
    head->size = source->size;
    i->size = source->indirect->size;
    head->offset = source->offset;
    i->offset = source->indirect->offset;
    head->is_signed = source->is_signed;
    i->is_signed = source->indirect->is_signed;
    head->indirect = i;
    i->indirect = head;
    head->members = source->members;
    i->members =  source->indirect->members;
    head->type = head;
    i->type = i;

    return head;
}

File /M2-Planet/cc_core.c

Live-bootstrap source file is 'seed/stage0-posix/M2-Planet/cc_core.c'.
URL: https://github.com/oriansj/M2-Planet/blob/0479606ed5131a64cd1dd2dd3f257e73cd9dce68/cc_core.c
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "cc.h"
#include "gcc_req.h"
#include <stdint.h>

/* Global lists */
struct token_list* global_symbol_list;
struct token_list* global_function_list;
struct token_list* global_constant_list;

/* Core lists for this file */
struct token_list* function;

/* What we are currently working on */
struct type* current_target;
char* break_target_head;
char* break_target_func;
char* break_target_num;
char* continue_target_head;
struct token_list* break_frame;
int current_count;
int Address_of;

/* Imported functions */
char* int2str(int x, int base, int signed_p);
int strtoint(char *a);
char* parse_string(char* string);
int escape_lookup(char* c);
void require(int bool, char* error);
struct token_list* reverse_list(struct token_list* head);
struct type* mirror_type(struct type* source, char* name);
struct type* add_primitive(struct type* a);

struct token_list* emit(char *s, struct token_list* head)
{
    struct token_list* t = calloc(1, sizeof(struct token_list));
    require(NULL != t, "Exhausted memory while generating token to emit\n");
    t->next = head;
    t->s = s;
    return t;
}

void emit_out(char* s)
{
    output_list = emit(s, output_list);
}

struct token_list* uniqueID(char* s, struct token_list* l, char* num)
{
    l = emit("\n", emit(num, emit("_", emit(s, l))));
    return l;
}

void uniqueID_out(char* s, char* num)
{
    output_list = uniqueID(s, output_list, num);
}

struct token_list* sym_declare(char *s, struct type* t, struct token_list* list)
{
    struct token_list* a = calloc(1, sizeof(struct token_list));
    require(NULL != a, "Exhausted memory while attempting to declare a symbol\n");
    a->next = list;
    a->s = s;
    a->type = t;
    return a;
}

struct token_list* sym_lookup(char *s, struct token_list* symbol_list)
{
    struct token_list* i;
    for(i = symbol_list; NULL != i; i = i->next)
    {
        if(match(i->s, s)) return i;
    }
    return NULL;
}

void line_error_token(struct token_list *token)
{
    if(NULL == token)
    {
        fputs("EOF reached inside of line_error\n", stderr);
        fputs("problem at end of file\n", stderr);
        return;
    }
    fputs(token->filename, stderr);
    fputs(":", stderr);
    fputs(int2str(token->linenumber, 10, TRUE), stderr);
    fputs(":", stderr);
}

void line_error()
{
    line_error_token(global_token);
}

void require_match(char* message, char* required)
{
    if(NULL == global_token)
    {
        line_error();
        fputs("EOF reached inside of require match\n", stderr);
        fputs("problem at end of file\n", stderr);
        fputs(message, stderr);
        exit(EXIT_FAILURE);
    }
    if(!match(global_token->s, required))
    {
        line_error();
        fputs(message, stderr);
        exit(EXIT_FAILURE);
    }
    global_token = global_token->next;
}

void maybe_bootstrap_error(char* feature)
{
    if (BOOTSTRAP_MODE)
    {
        line_error();
        fputs(feature, stderr);
        fputs(" is not supported in --bootstrap-mode\n", stderr);
        exit(EXIT_FAILURE);
    }
}

void expression();
void function_call(char* s, int bool)
{
    require_match("ERROR in process_expression_list\nNo ( was found\n", "(");
    require(NULL != global_token, "Improper function call\n");
    int passed = 0;

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
    {
        emit_out("PUSHR R13 R15\t# Prevent overwriting in recursion\n");
        emit_out("PUSHR R14 R15\t# Protect the old base pointer\n");
        emit_out("COPY R13 R15\t# Copy new base pointer\n");
    }
    else if(X86 == Architecture)
    {
        emit_out("push_edi\t# Prevent overwriting in recursion\n");
        emit_out("push_ebp\t# Protect the old base pointer\n");
        emit_out("mov_edi,esp\t# Copy new base pointer\n");
    }
    else if(AMD64 == Architecture)
    {
        emit_out("push_rdi\t# Prevent overwriting in recursion\n");
        emit_out("push_rbp\t# Protect the old base pointer\n");
        emit_out("mov_rdi,rsp\t# Copy new base pointer\n");
    }
    else if(ARMV7L == Architecture)
    {
        emit_out("{R11} PUSH_ALWAYS\t# Prevent overwriting in recursion\n");
        emit_out("{BP} PUSH_ALWAYS\t# Protect the old base pointer\n");
        emit_out("'0' SP R11 NO_SHIFT MOVE_ALWAYS\t# Copy new base pointer\n");
    }
    else if(AARCH64 == Architecture)
    {
        emit_out("PUSH_X16\t# Protect a tmp register we're going to use\n");
        emit_out("PUSH_LR\t# Protect the old return pointer (link)\n");
        emit_out("PUSH_BP\t# Protect the old base pointer\n");
        emit_out("SET_X16_FROM_SP\t# The base pointer to-be\n");
    }
    else if(RISCV32 == Architecture)
    {
        emit_out("rd_sp rs1_sp !-12 addi\t# Allocate stack\n");
        emit_out("rs1_sp rs2_ra @4 sw\t# Protect the old return pointer\n");
        emit_out("rs1_sp rs2_fp sw\t# Protect the old frame pointer\n");
        emit_out("rs1_sp rs2_tp @8 sw\t# Protect temp register we are going to use\n");
        emit_out("rd_tp rs1_sp mv\t# The base pointer to-be\n");
    }
    else if(RISCV64 == Architecture)
    {
        emit_out("rd_sp rs1_sp !-24 addi\t# Allocate stack\n");
        emit_out("rs1_sp rs2_ra @8 sd\t# Protect the old return pointer\n");
        emit_out("rs1_sp rs2_fp sd\t# Protect the old frame pointer\n");
        emit_out("rs1_sp rs2_tp @16 sd\t# Protect temp register we are going to use\n");
        emit_out("rd_tp rs1_sp mv\t# The base pointer to-be\n");
    }

    if(global_token->s[0] != ')')
    {
        expression();
        require(NULL != global_token, "incomplete function call, received EOF instead of )\n");
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R0 R15\t#_process_expression1\n");
        else if(X86 == Architecture) emit_out("push_eax\t#_process_expression1\n");
        else if(AMD64 == Architecture) emit_out("push_rax\t#_process_expression1\n");
        else if(ARMV7L == Architecture) emit_out("{R0} PUSH_ALWAYS\t#_process_expression1\n");
        else if(AARCH64 == Architecture) emit_out("PUSH_X0\t#_process_expression1\n");
        else if(RISCV32 == Architecture) emit_out("rd_sp rs1_sp !-4 addi\nrs1_sp rs2_a0 sw\t#_process_expression1\n");
        else if(RISCV64 == Architecture) emit_out("rd_sp rs1_sp !-8 addi\nrs1_sp rs2_a0 sd\t#_process_expression1\n");
        passed = 1;

        while(global_token->s[0] == ',')
        {
            global_token = global_token->next;
            require(NULL != global_token, "incomplete function call, received EOF instead of argument\n");
            expression();
            if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R0 R15\t#_process_expression2\n");
            else if(X86 == Architecture) emit_out("push_eax\t#_process_expression2\n");
            else if(AMD64 == Architecture) emit_out("push_rax\t#_process_expression2\n");
            else if(ARMV7L == Architecture) emit_out("{R0} PUSH_ALWAYS\t#_process_expression2\n");
            else if(AARCH64 == Architecture) emit_out("PUSH_X0\t#_process_expression2\n");
            else if(RISCV32 == Architecture) emit_out("rd_sp rs1_sp !-4 addi\nrs1_sp rs2_a0 sw\t#_process_expression2\n");
            else if(RISCV64 == Architecture) emit_out("rd_sp rs1_sp !-8 addi\nrs1_sp rs2_a0 sd\t#_process_expression2\n");
            passed = passed + 1;
        }
    }

    require_match("ERROR in process_expression_list\nNo ) was found\n", ")");

    if(TRUE == bool)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            emit_out("LOAD R0 R14 ");
            emit_out(s);
            emit_out("\nMOVE R14 R13\n");
            emit_out("CALL R0 R15\n");
        }
        else if(X86 == Architecture)
        {
            emit_out("lea_eax,[ebp+DWORD] %");
            emit_out(s);
            emit_out("\nmov_eax,[eax]\n");
            emit_out("mov_ebp,edi\n");
            emit_out("call_eax\n");
        }
        else if(AMD64 == Architecture)
        {
            emit_out("lea_rax,[rbp+DWORD] %");
            emit_out(s);
            emit_out("\nmov_rax,[rax]\n");
            emit_out("mov_rbp,rdi\n");
            emit_out("call_rax\n");
        }
        else if(ARMV7L == Architecture)
        {
            emit_out("!");
            emit_out(s);
            emit_out(" R0 SUB BP ARITH_ALWAYS\n");
            emit_out("!0 R0 LOAD32 R0 MEMORY\n");
            emit_out("{LR} PUSH_ALWAYS\t# Protect the old link register\n");
            emit_out("'0' R11 BP NO_SHIFT MOVE_ALWAYS\n");
            emit_out("'3' R0 CALL_REG_ALWAYS\n");
            emit_out("{LR} POP_ALWAYS\t# Prevent overwrite\n");
        }
        else if(AARCH64 == Architecture)
        {
            emit_out("SET_X0_FROM_BP\n");
            emit_out("LOAD_W1_AHEAD\nSKIP_32_DATA\n%");
            emit_out(s);
            emit_out("\nSUB_X0_X0_X1\n");
            emit_out("DEREF_X0\n");
            emit_out("SET_BP_FROM_X16\n");
            emit_out("SET_X16_FROM_X0\n");
            emit_out("BLR_X16\n");
        }
        else if(RISCV32 == Architecture)
        {
            emit_out("rd_a0 rs1_fp !");
            emit_out(s);
            emit_out(" addi\n");
            emit_out("rd_a0 rs1_a0 lw\n");
            emit_out("rd_fp rs1_tp mv\n");
            emit_out("rd_ra rs1_a0 jalr\n");
        }
        else if(RISCV64 == Architecture)
        {
            emit_out("rd_a0 rs1_fp !");
            emit_out(s);
            emit_out(" addi\n");
            emit_out("rd_a0 rs1_a0 ld\n");
            emit_out("rd_fp rs1_tp mv\n");
            emit_out("rd_ra rs1_a0 jalr\n");
        }
    }
    else
    {
        if((KNIGHT_NATIVE == Architecture) || (KNIGHT_POSIX == Architecture))
        {
            emit_out("MOVE R14 R13\n");
            emit_out("LOADR R0 4\nJUMP 4\n&FUNCTION_");
            emit_out(s);
            emit_out("\nCALL R0 R15\n");
        }
        else if(X86 == Architecture)
        {
            emit_out("mov_ebp,edi\n");
            emit_out("call %FUNCTION_");
            emit_out(s);
            emit_out("\n");
        }
        else if(AMD64 == Architecture)
        {
            emit_out("mov_rbp,rdi\n");
            emit_out("call %FUNCTION_");
            emit_out(s);
            emit_out("\n");
        }
        else if(ARMV7L == Architecture)
        {
            emit_out("{LR} PUSH_ALWAYS\t# Protect the old link register\n");
            emit_out("'0' R11 BP NO_SHIFT MOVE_ALWAYS\n");
            emit_out("^~FUNCTION_");
            emit_out(s);
            emit_out(" CALL_ALWAYS\n");
            emit_out("{LR} POP_ALWAYS\t# Restore the old link register\n");
        }
        else if(AARCH64 == Architecture)
        {
            emit_out("SET_BP_FROM_X16\n");
            emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&FUNCTION_");
            emit_out(s);
            emit_out("\n");
            emit_out("BLR_X16\n");
        }
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
        {
            emit_out("rd_fp rs1_tp mv\n");
            emit_out("rd_ra $FUNCTION_");
            emit_out(s);
            emit_out(" jal\n");
        }
    }

    for(; passed > 0; passed = passed - 1)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# _process_expression_locals\n");
        else if(X86 == Architecture) emit_out("pop_ebx\t# _process_expression_locals\n");
        else if(AMD64 == Architecture) emit_out("pop_rbx\t# _process_expression_locals\n");
        else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# _process_expression_locals\n");
        else if(AARCH64 == Architecture) emit_out("POP_X1\t# _process_expression_locals\n");
        else if(RISCV32 == Architecture) emit_out("rd_a1 rs1_sp lw\t# _process_expression_locals\nrd_sp rs1_sp !4 addi\n");
        else if(RISCV64 == Architecture) emit_out("rd_a1 rs1_sp ld\t# _process_expression_locals\nrd_sp rs1_sp !8 addi\n");
    }

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
    {
        emit_out("POPR R14 R15\t# Restore old base pointer\n");
        emit_out("POPR R13 R15\t# Prevent overwrite\n");
    }
    else if(X86 == Architecture)
    {
        emit_out("pop_ebp\t# Restore old base pointer\n");
        emit_out("pop_edi\t# Prevent overwrite\n");
    }
    else if(AMD64 == Architecture)
    {
        emit_out("pop_rbp\t# Restore old base pointer\n");
        emit_out("pop_rdi\t# Prevent overwrite\n");
    }
    else if(ARMV7L == Architecture)
    {
        emit_out("{BP} POP_ALWAYS\t# Restore old base pointer\n");
        emit_out("{R11} POP_ALWAYS\t# Prevent overwrite\n");
    }
    else if(AARCH64 == Architecture)
    {
        emit_out("POP_BP\t# Restore the old base pointer\n");
        emit_out("POP_LR\t# Restore the old return pointer (link)\n");
        emit_out("POP_X16\t# Restore a register we used as tmp\n");
    }
    else if(RISCV32 == Architecture)
    {
        emit_out("rd_fp rs1_sp lw\t# Restore old frame pointer\n");
        emit_out("rd_tp rs1_sp !8 lw\t# Restore temp register\n");
        emit_out("rd_ra rs1_sp !4 lw\t# Restore return address\n");
        emit_out("rd_sp rs1_sp !12 addi\t# Deallocate stack\n");
    }
    else if(RISCV64 == Architecture)
    {
        emit_out("rd_fp rs1_sp ld\t# Restore old frame pointer\n");
        emit_out("rd_tp rs1_sp !16 ld\t# Restore temp register\n");
        emit_out("rd_ra rs1_sp !8 ld\t# Restore return address\n");
        emit_out("rd_sp rs1_sp !24 addi\t# Deallocate stack\n");
    }
}

void constant_load(char* s)
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("LOADI R0 ");
    else if(X86 == Architecture) emit_out("mov_eax, %");
    else if(AMD64 == Architecture) emit_out("mov_rax, %");
    else if(ARMV7L == Architecture) emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
    else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n%");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        emit_out("rd_a0 ~");
        emit_out(s);
        emit_out(" lui\nrd_a0 rs1_a0 !");
    }
    emit_out(s);
    if(RISCV32 == Architecture) emit_out(" addi\n");
    else if(RISCV64 == Architecture) emit_out(" addiw\n");
    emit_out("\n");
}

char* load_value_signed(unsigned size)
{
    if(size == 1)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOAD8 R0 R0 0\n";
        else if(X86 == Architecture) return "movsx_eax,BYTE_PTR_[eax]\n";
        else if(AMD64 == Architecture) return "movsx_rax,BYTE_PTR_[rax]\n";
        else if(ARMV7L == Architecture) return "LOADS8 R0 LOAD R0 HALF_MEMORY\n";
        else if(AARCH64 == Architecture) return "LDRSB_X0_[X0]\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "rd_a0 rs1_a0 lb\n";
    }
    else if(size == 2)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOAD16 R0 R0 0\n";
        else if(X86 == Architecture) return "movsx_eax,WORD_PTR_[eax]\n";
        else if(AMD64 == Architecture) return "movsx_rax,WORD_PTR_[rax]\n";
        else if(ARMV7L == Architecture) return "LOADS16 R0 LOAD R0 HALF_MEMORY\n";
        else if(AARCH64 == Architecture) return "LDRSH_X0_[X0]\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "rd_a0 rs1_a0 lh\n";
    }
    else if(size == 4)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOAD R0 R0 0\n";
        else if(X86 == Architecture) return "mov_eax,[eax]\n";
        else if(AMD64 == Architecture) return "movsx_rax,DWORD_PTR_[rax]\n";
        else if(ARMV7L == Architecture) return "!0 R0 LOAD32 R0 MEMORY\n";
        else if(AARCH64 == Architecture) return "LDR_W0_[X0]\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "rd_a0 rs1_a0 lw\n";
    }
    else if(size == 8)
    {
        if(AMD64 == Architecture) return "mov_rax,[rax]\n";
        else if(AARCH64 == Architecture) return "DEREF_X0\n";
        else if(RISCV64 == Architecture) return "rd_a0 rs1_a0 ld\n";
    }
    line_error();
    fputs(" Got unsupported size ", stderr);
    fputs(int2str(size, 10, TRUE), stderr);
    fputs(" when trying to load value.\n", stderr);
    exit(EXIT_FAILURE);
}

char* load_value_unsigned(unsigned size)
{
    if(size == 1)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOADU8 R0 R0 0\n";
        else if(X86 == Architecture) return "movzx_eax,BYTE_PTR_[eax]\n";
        else if(AMD64 == Architecture) return "movzx_rax,BYTE_PTR_[rax]\n";
        else if(ARMV7L == Architecture) return "!0 R0 LOAD R0 MEMORY\n";
        else if(AARCH64 == Architecture) return "DEREF_X0_BYTE\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "rd_a0 rs1_a0 lbu\n";
    }
    else if(size == 2)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOADU16 R0 R0 0\n";
        else if(X86 == Architecture) return "movzx_eax,WORD_PTR_[eax]\n";
        else if(AMD64 == Architecture) return "movzx_rax,WORD_PTR_[rax]\n";
        else if(ARMV7L == Architecture) return "NO_OFFSET R0 LOAD R0 HALF_MEMORY\n";
        else if(AARCH64 == Architecture) return "LDRH_W0_[X0]\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "rd_a0 rs1_a0 lhu\n";
    }
    else if(size == 4)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOAD R0 R0 0\n";
        else if(X86 == Architecture) return "mov_eax,[eax]\n";
        else if(AMD64 == Architecture) return "mov_eax,[rax]\n";
        else if(ARMV7L == Architecture) return "!0 R0 LOAD32 R0 MEMORY\n";
        else if(AARCH64 == Architecture) return "LDR_W0_[X0]\n";
        else if(RISCV32 == Architecture) return "rd_a0 rs1_a0 lw\n";
        else if(RISCV64 == Architecture) return "rd_a0 rs1_a0 lwu\n";
    }
    else if(size == 8)
    {
        if(AMD64 == Architecture) return "mov_rax,[rax]\n";
        else if(AARCH64 == Architecture) return "DEREF_X0\n";
        else if(RISCV64 == Architecture) return "rd_a0 rs1_a0 ld\n";
    }
    line_error();
    fputs(" Got unsupported size ", stderr);
    fputs(int2str(size, 10, TRUE), stderr);
    fputs(" when trying to load value.\n", stderr);
    exit(EXIT_FAILURE);
}

char* load_value(unsigned size, int is_signed)
{
    if(is_signed) return load_value_signed(size);
    return load_value_unsigned(size);
}

char* store_value(unsigned size)
{
    if(size == 1)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "STORE8 R0 R1 0\n";
        else if(X86 == Architecture) return "mov_[ebx],al\n";
        else if(AMD64 == Architecture) return "mov_[rbx],al\n";
        else if(ARMV7L == Architecture) return "!0 R0 STORE8 R1 MEMORY\n";
        else if(AARCH64 == Architecture) return "STR_BYTE_W0_[X1]\n";
        else if(RISCV32 == Architecture) return "rs1_a1 rs2_a0 sb\n";
        else if(RISCV64 == Architecture) return "rs1_a1 rs2_a0 sb\n";
    }
    else if(size == 2)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "STORE16 R0 R1 0\n";
        else if(X86 == Architecture) return "mov_[ebx],ax\n";
        else if(AMD64 == Architecture) return "mov_[rbx],ax\n";
        else if(ARMV7L == Architecture) return "NO_OFFSET R0 STORE16 R1 HALF_MEMORY\n";
        else if(AARCH64 == Architecture) return "STRH_W0_[X1]\n";
        else if(RISCV32 == Architecture || RISCV64 == Architecture) return "rs1_a1 rs2_a0 sh\n";
    }
    else if(size == 4)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "STORE R0 R1 0\n";
        else if(X86 == Architecture) return "mov_[ebx],eax\n";
        else if(AMD64 == Architecture) return "mov_[rbx],eax\n";
        else if(ARMV7L == Architecture) return "!0 R0 STORE32 R1 MEMORY\n";
        else if(AARCH64 == Architecture) return "STR_W0_[X1]\n";
        else if(RISCV32 == Architecture || RISCV64 == Architecture) return "rs1_a1 rs2_a0 sw\n";
    }
    else if(size == 8)
    {
        if(AMD64 == Architecture) return "mov_[rbx],rax\n";
        else if(AARCH64 == Architecture) return "STR_X0_[X1]\n";
        else if(RISCV64 == Architecture) return "rs1_a1 rs2_a0 sd\n";
    }
    /* Should not happen but print error message. */
    fputs("Got unsupported size ", stderr);
    fputs(int2str(size, 10, TRUE), stderr);
    fputs(" when storing number in register.\n", stderr);
    line_error();
    exit(EXIT_FAILURE);
}

int is_compound_assignment(char* token)
{
    if(match("+=", token)) return TRUE;
    else if(match("-=", token)) return TRUE;
    else if(match("*=", token)) return TRUE;
    else if(match("/=", token)) return TRUE;
    else if(match("%=", token)) return TRUE;
    else if(match("<<=", token)) return TRUE;
    else if(match(">>=", token)) return TRUE;
    else if(match("&=", token)) return TRUE;
    else if(match("^=", token)) return TRUE;
    else if(match("|=", token)) return TRUE;
    return FALSE;
}

void postfix_expr_stub();
void variable_load(struct token_list* a, int num_dereference)
{
    require(NULL != global_token, "incomplete variable load received\n");
    if((match("FUNCTION", a->type->name) || match("FUNCTION*", a->type->name)) && match("(", global_token->s))
    {
        function_call(int2str(a->depth, 10, TRUE), TRUE);
        return;
    }
    current_target = a->type;

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("ADDI R0 R14 ");
    else if(X86 == Architecture) emit_out("lea_eax,[ebp+DWORD] %");
    else if(AMD64 == Architecture) emit_out("lea_rax,[rbp+DWORD] %");
    else if(ARMV7L == Architecture) emit_out("!");
    else if(AARCH64 == Architecture) emit_out("SET_X0_FROM_BP\nLOAD_W1_AHEAD\nSKIP_32_DATA\n%");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rd_a0 rs1_fp !");

    emit_out(int2str(a->depth, 10, TRUE));
    if(ARMV7L == Architecture) emit_out(" R0 SUB BP ARITH_ALWAYS");
    else if(AARCH64 == Architecture) emit_out("\nSUB_X0_X0_X1\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" addi");
    emit_out("\n");

    if(TRUE == Address_of) return;
    if(match(".", global_token->s))
    {
        postfix_expr_stub();
        return;
    }
    if(!match("=", global_token->s) && !is_compound_assignment(global_token->s))
    {
        emit_out(load_value(current_target->size, current_target->is_signed));
    }

    while (num_dereference > 0)
    {
        current_target = current_target->type;
        emit_out(load_value(current_target->size, current_target->is_signed));
        num_dereference = num_dereference - 1;
    }
}

void function_load(struct token_list* a)
{
    require(NULL != global_token, "incomplete function load\n");
    if(match("(", global_token->s))
    {
        function_call(a->s, FALSE);
        return;
    }

    if((KNIGHT_NATIVE == Architecture) || (KNIGHT_POSIX == Architecture)) emit_out("LOADR R0 4\nJUMP 4\n&FUNCTION_");
    else if(X86 == Architecture) emit_out("mov_eax, &FUNCTION_");
    else if(AMD64 == Architecture) emit_out("lea_rax,[rip+DWORD] %FUNCTION_");
    else if(ARMV7L == Architecture) emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n&FUNCTION_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n&FUNCTION_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rd_a0 ~FUNCTION_");
    emit_out(a->s);
    if(RISCV32 == Architecture)
    {
        emit_out(" auipc\n");
        emit_out("rd_a0 rs1_a0 !FUNCTION_");
        emit_out(a->s);
        emit_out(" addi");
    }
    else if(RISCV64 == Architecture)
    {
        emit_out(" auipc\n");
        emit_out("rd_a0 rs1_a0 !FUNCTION_");
        emit_out(a->s);
        emit_out(" addiw");
    }
    emit_out("\n");
}

void global_load(struct token_list* a)
{
    current_target = a->type;
    if((KNIGHT_NATIVE == Architecture) || (KNIGHT_POSIX == Architecture)) emit_out("LOADR R0 4\nJUMP 4\n&GLOBAL_");
    else if(X86 == Architecture) emit_out("mov_eax, &GLOBAL_");
    else if(AMD64 == Architecture) emit_out("lea_rax,[rip+DWORD] %GLOBAL_");
    else if(ARMV7L == Architecture) emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n&GLOBAL_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n&GLOBAL_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rd_a0 ~GLOBAL_");
    emit_out(a->s);
    if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        emit_out(" auipc\n");
        emit_out("rd_a0 rs1_a0 !GLOBAL_");
        emit_out(a->s);
        emit_out(" addi");
    }
    emit_out("\n");

    require(NULL != global_token, "unterminated global load\n");
    if(TRUE == Address_of) return;
    if(match(".", global_token->s))
    {
        postfix_expr_stub();
        return;
    }
    if(match("=", global_token->s) || is_compound_assignment(global_token->s)) return;

    emit_out(load_value(register_size, current_target->is_signed));
}

/*
 * primary-expr:
 * FAILURE
 * "String"
 * 'Char'
 * [0-9]*
 * [a-z,A-Z]*
 * ( expression )
 */

void primary_expr_failure()
{
    require(NULL != global_token, "hit EOF when expecting primary expression\n");
    line_error();
    fputs("Received ", stderr);
    fputs(global_token->s, stderr);
    fputs(" in primary_expr\n", stderr);
    exit(EXIT_FAILURE);
}

void primary_expr_string()
{
    char* number_string = int2str(current_count, 10, TRUE);
    current_count = current_count + 1;
    if((KNIGHT_NATIVE == Architecture) || (KNIGHT_POSIX == Architecture)) emit_out("LOADR R0 4\nJUMP 4\n&STRING_");
    else if(X86 == Architecture) emit_out("mov_eax, &STRING_");
    else if(AMD64 == Architecture) emit_out("lea_rax,[rip+DWORD] %STRING_");
    else if(ARMV7L == Architecture) emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n&STRING_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n&STRING_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rd_a0 ~STRING_");
    uniqueID_out(function->s, number_string);
    if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        emit_out("auipc\n");
        emit_out("rd_a0 rs1_a0 !STRING_");
        uniqueID_out(function->s, number_string);
        emit_out("addi\n");
    }

    /* The target */
    strings_list = emit(":STRING_", strings_list);
    strings_list = uniqueID(function->s, strings_list, number_string);

    /* catch case of just "foo" from segfaulting */
    require(NULL != global_token->next, "a string by itself is not valid C\n");

    /* Parse the string */
    if('"' != global_token->next->s[0])
    {
        strings_list = emit(parse_string(global_token->s), strings_list);
        global_token = global_token->next;
    }
    else
    {
        char* s = calloc(MAX_STRING, sizeof(char));

        /* prefix leading string */
        s[0] = '"';
        int i = 1;

        int j;
        while('"' == global_token->s[0])
        {
            /* Step past the leading '"' */
            j = 1;

            /* Copy the rest of the string as is */
            while(0 != global_token->s[j])
            {
                require(i < MAX_STRING, "concat string exceeded max string length\n");
                s[i] = global_token->s[j];
                i = i + 1;
                j = j + 1;
            }

            /* Move on to the next token */
            global_token = global_token->next;
            require(NULL != global_token, "multi-string null is not valid C\n");
        }

        /* Now use it */
        strings_list = emit(parse_string(s), strings_list);
    }
}

void primary_expr_char()
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("LOADI R0 ");
    else if(X86 == Architecture) emit_out("mov_eax, %");
    else if(AMD64 == Architecture) emit_out("mov_rax, %");
    else if(ARMV7L == Architecture) emit_out("!");
    else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n%");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rd_a0 !");
    emit_out(int2str(escape_lookup(global_token->s + 1), 10, TRUE));
    if(ARMV7L == Architecture) emit_out(" R0 LOADI8_ALWAYS");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" addi");
    emit_out("\n");
    global_token = global_token->next;
}

int hex2char(int c)
{
    if((c >= 0) && (c <= 9)) return (c + 48);
    else if((c >= 10) && (c <= 15)) return (c + 55);
    else return -1;
}

char* number_to_hex(int a, int bytes)
{
    require(bytes > 0, "number to hex must have a positive number of bytes greater than zero\n");
    char* result = calloc(1 + (bytes << 1), sizeof(char));
    if(NULL == result)
    {
        fputs("calloc failed in number_to_hex\n", stderr);
        exit(EXIT_FAILURE);
    }
    int i = 0;

    int divisor = (bytes << 3);
    require(divisor > 0, "unexpected wrap around in number_to_hex\n");

    /* Simply collect numbers until divisor is gone */
    while(0 != divisor)
    {
        divisor = divisor - 4;
        result[i] = hex2char((a >> divisor) & 0xF);
        i = i + 1;
    }

    return result;
}

void primary_expr_number(char* s)
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
    {
        int size = strtoint(s);
        if((32767 > size) && (size > -32768))
        {
            emit_out("LOADI R0 ");
            emit_out(s);
        }
        else
        {
            emit_out("LOADR R0 4\nJUMP 4\n'");
            emit_out(number_to_hex(size, register_size));
            emit_out("'");
        }
    }
    else if(X86 == Architecture)
    {
        emit_out("mov_eax, %");
        emit_out(s);
    }
    else if(AMD64 == Architecture)
    {
        emit_out("mov_rax, %");
        emit_out(s);
    }
    else if(ARMV7L == Architecture)
    {
        emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
        emit_out(s);
    }
    else if(AARCH64 == Architecture)
    {
        emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n%");
        emit_out(s);
    }
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        int size = strtoint(s);
        if((2047 > size) && (size > -2048))
        {
            emit_out("rd_a0 !");
            emit_out(s);
            emit_out(" addi");
        }
        else if (0 == (size >> 30))
        {
            emit_out("rd_a0 ~");
            emit_out(s);
            emit_out(" lui\n");
            emit_out("rd_a0 rs1_a0 !");
            emit_out(s);
            emit_out(" addi");
        }
        else
        {
            int high = size >> 30;
            int low = ((size >> 30) << 30) ^ size;
            emit_out("rd_a0 ~");
            emit_out(int2str(high, 10, TRUE));
            emit_out(" lui\n");
            emit_out("rd_a0 rs1_a0 !");
            emit_out(int2str(high, 10, TRUE));
            emit_out(" addi\n");
            emit_out("rd_a0 rs1_a0 rs2_x30 slli\n");
            emit_out("rd_t1 ~");
            emit_out(int2str(low, 10, TRUE));
            emit_out(" lui\n");
            emit_out("rd_t1 rs1_t1 !");
            emit_out(int2str(low, 10, TRUE));
            emit_out(" addi\n");
            emit_out("rd_a0 rs1_a0 rs2_t1 or\n");
        }
    }
    emit_out("\n");
}

void primary_expr_variable()
{
    int num_dereference = 0;
    while(global_token->s[0] == '*') {
        global_token = global_token->next;
        require(NULL != global_token, "Walked off the end of a variable dereference\n");
        num_dereference = num_dereference + 1;
    }
    char* s = global_token->s;
    global_token = global_token->next;
    struct token_list* a = sym_lookup(s, global_constant_list);
    if(NULL != a)
    {
        constant_load(a->arguments->s);
        return;
    }

    a = sym_lookup(s, function->locals);
    if(NULL != a)
    {
        variable_load(a, num_dereference);
        return;
    }

    a = sym_lookup(s, function->arguments);
    if(NULL != a)
    {
        variable_load(a, num_dereference);
        return;
    }

    a = sym_lookup(s, global_function_list);
    if(NULL != a)
    {
        function_load(a);
        return;
    }

    a = sym_lookup(s, global_symbol_list);
    if(NULL != a)
    {
        global_load(a);
        return;
    }

    line_error();
    fputs(s ,stderr);
    fputs(" is not a defined symbol\n", stderr);
    exit(EXIT_FAILURE);
}

void primary_expr();
struct type* promote_type(struct type* a, struct type* b)
{
    require(NULL != b, "impossible case 1 in promote_type\n");
    require(NULL != a, "impossible case 2 in promote_type\n");

    if(a == b) return a;

    struct type* i;
    for(i = global_types; NULL != i; i = i->next)
    {
        if(a->name == i->name) break;
        if(b->name == i->name) break;
        if(a->name == i->indirect->name) break;
        if(b->name == i->indirect->name) break;
        if(a->name == i->indirect->indirect->name) break;
        if(b->name == i->indirect->indirect->name) break;
    }
    require(NULL != i, "impossible case 3 in promote_type\n");
    return i;
}

void common_recursion(FUNCTION f)
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R0 R15\t#_common_recursion\n");
    else if(X86 == Architecture) emit_out("push_eax\t#_common_recursion\n");
    else if(AMD64 == Architecture) emit_out("push_rax\t#_common_recursion\n");
    else if(ARMV7L == Architecture) emit_out("{R0} PUSH_ALWAYS\t#_common_recursion\n");
    else if(AARCH64 == Architecture) emit_out("PUSH_X0\t#_common_recursion\n");
    else if(RISCV32 == Architecture) emit_out("rd_sp rs1_sp !-4 addi\t# _common_recursion\nrs1_sp rs2_a0 sw\n");
    else if(RISCV64 == Architecture) emit_out("rd_sp rs1_sp !-8 addi\t# _common_recursion\nrs1_sp rs2_a0 sd\n");

    struct type* last_type = current_target;
    global_token = global_token->next;
    require(NULL != global_token, "Received EOF in common_recursion\n");
    f();
    current_target = promote_type(current_target, last_type);

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# _common_recursion\n");
    else if(X86 == Architecture) emit_out("pop_ebx\t# _common_recursion\n");
    else if(AMD64 == Architecture) emit_out("pop_rbx\t# _common_recursion\n");
    else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# _common_recursion\n");
    else if(AARCH64 == Architecture) emit_out("POP_X1\t# _common_recursion\n");
    else if(RISCV32 == Architecture) emit_out("rd_a1 rs1_sp lw\nrd_sp rs1_sp !4 addi\t# _common_recursion\n");
    else if(RISCV64 == Architecture) emit_out("rd_a1 rs1_sp ld\nrd_sp rs1_sp !8 addi\t# _common_recursion\n");
}

void general_recursion(FUNCTION f, char* s, char* name, FUNCTION iterate)
{
    require(NULL != global_token, "Received EOF in general_recursion\n");
    if(match(name, global_token->s))
    {
        common_recursion(f);
        emit_out(s);
        iterate();
    }
}

void arithmetic_recursion(FUNCTION f, char* s1, char* s2, char* name, FUNCTION iterate)
{
    require(NULL != global_token, "Received EOF in arithmetic_recursion\n");
    if(match(name, global_token->s))
    {
        common_recursion(f);
        if(NULL == current_target)
        {
            emit_out(s1);
        }
        else if(current_target->is_signed)
        {
            emit_out(s1);
        }
        else
        {
            emit_out(s2);
        }
        iterate();
    }
}


/*
 * postfix-expr:
 *         primary-expr
 *         postfix-expr [ expression ]
 *         postfix-expr ( expression-list-opt )
 *         postfix-expr -> member
 *         postfix-expr . member
 */
struct type* lookup_member(struct type* parent, char* name);
void postfix_expr_arrow()
{
    emit_out("# looking up offset\n");
    global_token = global_token->next;
    require(NULL != global_token, "naked -> not allowed\n");

    struct type* i = lookup_member(current_target, global_token->s);
    current_target = i->type;
    global_token = global_token->next;
    require(NULL != global_token, "Unterminated -> expression not allowed\n");

    if(0 != i->offset)
    {
        emit_out("# -> offset calculation\n");
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            emit_out("ADDUI R0 R0 ");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\n");
        }
        else if(X86 == Architecture)
        {
            emit_out("mov_ebx, %");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\nadd_eax,ebx\n");
        }
        else if(AMD64 == Architecture)
        {
            emit_out("mov_rbx, %");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\nadd_rax,rbx\n");
        }
        else if(ARMV7L == Architecture)
        {
            emit_out("!0 R1 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\n'0' R0 R0 ADD R1 ARITH2_ALWAYS\n");
        }
        else if(AARCH64 == Architecture)
        {
            emit_out("LOAD_W1_AHEAD\nSKIP_32_DATA\n%");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\nADD_X0_X1_X0\n");
        }
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
        {
            emit_out("rd_a1 !");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out(" addi\n");
            emit_out("rd_a0 rs1_a1 rs2_a0 add\n");
        }
    }

    /* We don't yet support assigning structs to structs */
    if((!match("=", global_token->s) && !is_compound_assignment(global_token->s) && (register_size >= i->size)))
    {
        emit_out(load_value(i->size, i->is_signed));
    }
}

void postfix_expr_dot()
{
    maybe_bootstrap_error("Member access using .");
    emit_out("# looking up offset\n");
    global_token = global_token->next;
    require(NULL != global_token, "naked . not allowed\n");

    struct type* i = lookup_member(current_target, global_token->s);
    current_target = i->type;
    global_token = global_token->next;
    require(NULL != global_token, "Unterminated . expression not allowed\n");

    if(0 != i->offset)
    {
        emit_out("# . offset calculation\n");
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            emit_out("ADDUI R0 R0 ");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\n");
        }
        else if(X86 == Architecture)
        {
            emit_out("mov_ebx, %");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\nadd_eax,ebx\n");
        }
        else if(AMD64 == Architecture)
        {
            emit_out("mov_rbx, %");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\nadd_rax,rbx\n");
        }
        else if(ARMV7L == Architecture)
        {
            emit_out("!0 R1 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\n'0' R0 R0 ADD R1 ARITH2_ALWAYS\n");
        }
        else if(AARCH64 == Architecture)
        {
            emit_out("LOAD_W1_AHEAD\nSKIP_32_DATA\n%");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\nADD_X0_X1_X0\n");
        }
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
        {
            emit_out("rd_a1 !");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out(" addi\n");
            emit_out("rd_a0 rs1_a1 rs2_a0 add\n");
        }
    }
    if(match("=", global_token->s) || is_compound_assignment(global_token->s)) return;
    if(match("[", global_token->s)) return;

    emit_out(load_value(current_target->size, current_target->is_signed));
}

void postfix_expr_array()
{
    struct type* array = current_target;
    common_recursion(expression);
    current_target = array;
    require(NULL != current_target, "Arrays only apply to variables\n");

    char* assign = load_value(register_size, current_target->is_signed);

    /* Add support for Ints */
    if(match("char*", current_target->name))
    {
        assign = load_value(1, TRUE);
    }
    else
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R1 R15\nLOADI R1 ");
        else if(X86 == Architecture) emit_out("push_ebx\nmov_ebx, %");
        else if(AMD64 == Architecture) emit_out("push_rbx\nmov_rbx, %");
        else if(ARMV7L == Architecture) emit_out("{R1} PUSH_ALWAYS\n!0 R1 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
        else if(AARCH64 == Architecture) emit_out("PUSH_X1\nLOAD_W1_AHEAD\nSKIP_32_DATA\n%");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rd_a2 rs1_a1 addi\nrd_a1 !");
        emit_out(int2str(current_target->type->size, 10, TRUE));
        if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" addi");

        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("\nMULU R0 R1 R0\nPOPR R1 R15\n");
        else if(X86 == Architecture) emit_out("\nmul_ebx\npop_ebx\n");
        else if(AMD64 == Architecture) emit_out("\nmul_rbx\npop_rbx\n");
        else if(ARMV7L == Architecture) emit_out("\n'9' R0 '0' R1 MUL R0 ARITH2_ALWAYS\n{R1} POP_ALWAYS\n");
        else if(AARCH64 == Architecture) emit_out("\nMUL_X0_X1_X0\nPOP_X1\n");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("\nrd_a0 rs1_a1 rs2_a0 mul\nrd_a1 rs1_a2 addi\n");
    }

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("ADD R0 R0 R1\n");
    else if(X86 == Architecture) emit_out("add_eax,ebx\n");
    else if(AMD64 == Architecture) emit_out("add_rax,rbx\n");
    else if(ARMV7L == Architecture) emit_out("'0' R0 R0 ADD R1 ARITH2_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("ADD_X0_X1_X0\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rd_a0 rs1_a1 rs2_a0 add\n");

    require_match("ERROR in postfix_expr\nMissing ]\n", "]");
    require(NULL != global_token, "truncated array expression\n");

    if(match("=", global_token->s) || is_compound_assignment(global_token->s) || match(".", global_token->s))
    {
        assign = "";
    }
    if(match("[", global_token->s))
    {
        current_target = current_target->type;
    }

    emit_out(assign);
}

/*
 * unary-expr:
 *         &postfix-expr
 *         - postfix-expr
 *         !postfix-expr
 *         sizeof ( type )
 */
struct type* type_name();
void unary_expr_sizeof()
{
    global_token = global_token->next;
    require(NULL != global_token, "Received EOF when starting sizeof\n");
    require_match("ERROR in unary_expr\nMissing (\n", "(");
    struct type* a = type_name();
    require_match("ERROR in unary_expr\nMissing )\n", ")");

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("LOADUI R0 ");
    else if(X86 == Architecture) emit_out("mov_eax, %");
    else if(AMD64 == Architecture) emit_out("mov_rax, %");
    else if(ARMV7L == Architecture) emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
    else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n%");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rd_a0 !");
    emit_out(int2str(a->size, 10, TRUE));
    if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" addi");
    emit_out("\n");
}

void postfix_expr_stub()
{
    require(NULL != global_token, "Unexpected EOF, improperly terminated primary expression\n");
    if(match("[", global_token->s))
    {
        postfix_expr_array();
        postfix_expr_stub();
    }

    if(match("->", global_token->s))
    {
        postfix_expr_arrow();
        postfix_expr_stub();
    }

    if(match(".", global_token->s))
    {
        postfix_expr_dot();
        postfix_expr_stub();
    }
}

void postfix_expr()
{
    primary_expr();
    postfix_expr_stub();
}

/*
 * additive-expr:
 *         postfix-expr
 *         additive-expr * postfix-expr
 *         additive-expr / postfix-expr
 *         additive-expr % postfix-expr
 *         additive-expr + postfix-expr
 *         additive-expr - postfix-expr
 *         additive-expr << postfix-expr
 *         additive-expr >> postfix-expr
 */
void additive_expr_stub_a()
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
    {
        arithmetic_recursion(postfix_expr, "MUL R0 R1 R0\n", "MULU R0 R1 R0\n", "*", additive_expr_stub_a);
        arithmetic_recursion(postfix_expr, "DIV R0 R1 R0\n", "DIVU R0 R1 R0\n", "/", additive_expr_stub_a);
        arithmetic_recursion(postfix_expr, "MOD R0 R1 R0\n", "MODU R0 R1 R0\n", "%", additive_expr_stub_a);
    }
    else if(X86 == Architecture)
    {
        arithmetic_recursion(postfix_expr, "imul_ebx\n", "mul_ebx\n", "*", additive_expr_stub_a);
        arithmetic_recursion(postfix_expr, "xchg_ebx,eax\ncdq\nidiv_ebx\n", "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\n", "/", additive_expr_stub_a);
        arithmetic_recursion(postfix_expr, "xchg_ebx,eax\ncdq\nidiv_ebx\nmov_eax,edx\n", "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\nmov_eax,edx\n", "%", additive_expr_stub_a);
    }
    else if(AMD64 == Architecture)
    {
        arithmetic_recursion(postfix_expr, "imul_rbx\n", "mul_rbx\n", "*", additive_expr_stub_a);
        arithmetic_recursion(postfix_expr, "xchg_rbx,rax\ncqo\nidiv_rbx\n", "xchg_rbx,rax\nmov_rdx, %0\ndiv_rbx\n", "/", additive_expr_stub_a);
        arithmetic_recursion(postfix_expr, "xchg_rbx,rax\ncqo\nidiv_rbx\nmov_rax,rdx\n", "xchg_rbx,rax\nmov_rdx, %0\ndiv_rbx\nmov_rax,rdx\n", "%", additive_expr_stub_a);
    }
    else if(ARMV7L == Architecture)
    {
        arithmetic_recursion(postfix_expr, "'9' R0 '0' R1 MULS R0 ARITH2_ALWAYS\n", "'9' R0 '0' R1 MUL R0 ARITH2_ALWAYS\n", "*", additive_expr_stub_a);
        arithmetic_recursion(postfix_expr, "{LR} PUSH_ALWAYS\n^~divides CALL_ALWAYS\n{LR} POP_ALWAYS\n", "{LR} PUSH_ALWAYS\n^~divide CALL_ALWAYS\n{LR} POP_ALWAYS\n", "/", additive_expr_stub_a);
        arithmetic_recursion(postfix_expr, "{LR} PUSH_ALWAYS\n^~moduluss CALL_ALWAYS\n{LR} POP_ALWAYS\n", "{LR} PUSH_ALWAYS\n^~modulus CALL_ALWAYS\n{LR} POP_ALWAYS\n", "%", additive_expr_stub_a);
    }
    else if(AARCH64 == Architecture)
    {
        general_recursion(postfix_expr, "MUL_X0_X1_X0\n", "*", additive_expr_stub_a);
        arithmetic_recursion(postfix_expr, "SDIV_X0_X1_X0\n", "UDIV_X0_X1_X0\n", "/", additive_expr_stub_a);
        arithmetic_recursion(postfix_expr, "SDIV_X2_X1_X0\nMSUB_X0_X0_X2_X1\n", "UDIV_X2_X1_X0\nMSUB_X0_X0_X2_X1\n", "%", additive_expr_stub_a);
    }
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        general_recursion(postfix_expr, "rd_a0 rs1_a1 rs2_a0 mul\n", "*", additive_expr_stub_a);
        arithmetic_recursion(postfix_expr, "rd_a0 rs1_a1 rs2_a0 div\n", "rd_a0 rs1_a1 rs2_a0 divu\n", "/", additive_expr_stub_a);
        arithmetic_recursion(postfix_expr, "rd_a0 rs1_a1 rs2_a0 rem\n", "rd_a0 rs1_a1 rs2_a0 remu\n", "%", additive_expr_stub_a);
    }
}


void additive_expr_a()
{
    postfix_expr();
    additive_expr_stub_a();
}

void additive_expr_stub_b()
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
    {
        arithmetic_recursion(additive_expr_a, "ADD R0 R1 R0\n", "ADDU R0 R1 R0\n", "+", additive_expr_stub_b);
        arithmetic_recursion(additive_expr_a, "SUB R0 R1 R0\n", "SUBU R0 R1 R0\n", "-", additive_expr_stub_b);
    }
    else if(X86 == Architecture)
    {
        arithmetic_recursion(additive_expr_a, "add_eax,ebx\n", "add_eax,ebx\n", "+", additive_expr_stub_b);
        arithmetic_recursion(additive_expr_a, "sub_ebx,eax\nmov_eax,ebx\n", "sub_ebx,eax\nmov_eax,ebx\n", "-", additive_expr_stub_b);
    }
    else if(AMD64 == Architecture)
    {
        arithmetic_recursion(additive_expr_a, "add_rax,rbx\n", "add_rax,rbx\n", "+", additive_expr_stub_b);
        arithmetic_recursion(additive_expr_a, "sub_rbx,rax\nmov_rax,rbx\n", "sub_rbx,rax\nmov_rax,rbx\n", "-", additive_expr_stub_b);
    }
    else if(ARMV7L == Architecture)
    {
        arithmetic_recursion(additive_expr_a, "'0' R0 R0 ADD R1 ARITH2_ALWAYS\n", "'0' R0 R0 ADD R1 ARITH2_ALWAYS\n", "+", additive_expr_stub_b);
        arithmetic_recursion(additive_expr_a, "'0' R0 R0 SUB R1 ARITH2_ALWAYS\n", "'0' R0 R0 SUB R1 ARITH2_ALWAYS\n", "-", additive_expr_stub_b);
    }
    else if(AARCH64 == Architecture)
    {
        general_recursion(additive_expr_a, "ADD_X0_X1_X0\n", "+", additive_expr_stub_b);
        general_recursion(additive_expr_a, "SUB_X0_X1_X0\n", "-", additive_expr_stub_b);
    }
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        general_recursion(additive_expr_a, "rd_a0 rs1_a1 rs2_a0 add\n", "+", additive_expr_stub_b);
        general_recursion(additive_expr_a, "rd_a0 rs1_a1 rs2_a0 sub\n", "-", additive_expr_stub_b);
    }
}


void additive_expr_b()
{
    additive_expr_a();
    additive_expr_stub_b();
}

void additive_expr_stub_c()
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
    {
        arithmetic_recursion(additive_expr_b, "SAL R0 R1 R0\n", "SL0 R0 R1 R0\n", "<<", additive_expr_stub_c);
        arithmetic_recursion(additive_expr_b, "SAR R0 R1 R0\n", "SR0 R0 R1 R0\n", ">>", additive_expr_stub_c);
    }
    else if(X86 == Architecture)
    {
        arithmetic_recursion(additive_expr_b, "mov_ecx,eax\nmov_eax,ebx\nsal_eax,cl\n", "mov_ecx,eax\nmov_eax,ebx\nshl_eax,cl\n", "<<", additive_expr_stub_c);
        arithmetic_recursion(additive_expr_b, "mov_ecx,eax\nmov_eax,ebx\nsar_eax,cl\n", "mov_ecx,eax\nmov_eax,ebx\nshr_eax,cl\n", ">>", additive_expr_stub_c);
    }
    else if(AMD64 == Architecture)
    {
        arithmetic_recursion(additive_expr_b, "mov_rcx,rax\nmov_rax,rbx\nsal_rax,cl\n", "mov_rcx,rax\nmov_rax,rbx\nshl_rax,cl\n", "<<", additive_expr_stub_c);
        arithmetic_recursion(additive_expr_b, "mov_rcx,rax\nmov_rax,rbx\nsar_rax,cl\n", "mov_rcx,rax\nmov_rax,rbx\nshr_rax,cl\n", ">>", additive_expr_stub_c);
    }
    else if(ARMV7L == Architecture)
    {
        arithmetic_recursion(additive_expr_b, "LEFT R1 R0 R0 SHIFT AUX_ALWAYS\n", "LEFT R1 R0 R0 SHIFT AUX_ALWAYS\n", "<<", additive_expr_stub_c);
        arithmetic_recursion(additive_expr_b, "ARITH_RIGHT R1 R0 R0 SHIFT AUX_ALWAYS\n", "RIGHT R1 R0 R0 SHIFT AUX_ALWAYS\n", ">>", additive_expr_stub_c);
    }
    else if(AARCH64 == Architecture)
    {
        general_recursion(additive_expr_b, "LSHIFT_X0_X1_X0\n", "<<", additive_expr_stub_c);
        arithmetic_recursion(additive_expr_b, "ARITH_RSHIFT_X0_X1_X0\n", "LOGICAL_RSHIFT_X0_X1_X0\n", ">>", additive_expr_stub_c);
    }
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        general_recursion(additive_expr_b, "rd_a0 rs1_a1 rs2_a0 sll\n", "<<", additive_expr_stub_c);
        arithmetic_recursion(additive_expr_b, "rd_a0 rs1_a1 rs2_a0 sra\n", "rd_a0 rs1_a1 rs2_a0 srl\n", ">>", additive_expr_stub_c);
    }
}


void additive_expr_c()
{
    additive_expr_b();
    additive_expr_stub_c();
}


/*
 * relational-expr:
 *         additive_expr
 *         relational-expr < additive_expr
 *         relational-expr <= additive_expr
 *         relational-expr >= additive_expr
 *         relational-expr > additive_expr
 */

void relational_expr_stub()
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
    {
        arithmetic_recursion(additive_expr_c, "CMP R0 R1 R0\nSET.L R0 R0 1\n", "CMPU R0 R1 R0\nSET.L R0 R0 1\n", "<", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "CMP R0 R1 R0\nSET.LE R0 R0 1\n", "CMPU R0 R1 R0\nSET.LE R0 R0 1\n", "<=", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "CMP R0 R1 R0\nSET.GE R0 R0 1\n", "CMPU R0 R1 R0\nSET.GE R0 R0 1\n", ">=", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "CMP R0 R1 R0\nSET.G R0 R0 1\n", "CMPU R0 R1 R0\nSET.G R0 R0 1\n", ">", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "CMP R0 R1 R0\nSET.E R0 R0 1\n", "CMPU R0 R1 R0\nSET.E R0 R0 1\n", "==", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "CMP R0 R1 R0\nSET.NE R0 R0 1\n", "CMPU R0 R1 R0\nSET.NE R0 R0 1\n", "!=", relational_expr_stub);
    }
    else if(X86 == Architecture)
    {
        arithmetic_recursion(additive_expr_c, "cmp\nsetl_al\nmovzx_eax,al\n", "cmp\nsetb_al\nmovzx_eax,al\n", "<", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "cmp\nsetle_al\nmovzx_eax,al\n", "cmp\nsetbe_al\nmovzx_eax,al\n", "<=", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "cmp\nsetge_al\nmovzx_eax,al\n", "cmp\nsetae_al\nmovzx_eax,al\n", ">=", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "cmp\nsetg_al\nmovzx_eax,al\n", "cmp\nseta_al\nmovzx_eax,al\n", ">", relational_expr_stub);
        general_recursion(additive_expr_c, "cmp\nsete_al\nmovzx_eax,al\n", "==", relational_expr_stub);
        general_recursion(additive_expr_c, "cmp\nsetne_al\nmovzx_eax,al\n", "!=", relational_expr_stub);
    }
    else if(AMD64 == Architecture)
    {
        arithmetic_recursion(additive_expr_c, "cmp_rbx,rax\nsetl_al\nmovzx_rax,al\n", "cmp_rbx,rax\nsetb_al\nmovzx_rax,al\n", "<", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "cmp_rbx,rax\nsetle_al\nmovzx_rax,al\n", "cmp_rbx,rax\nsetbe_al\nmovzx_rax,al\n", "<=", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "cmp_rbx,rax\nsetge_al\nmovzx_rax,al\n", "cmp_rbx,rax\nsetae_al\nmovzx_rax,al\n", ">=", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "cmp_rbx,rax\nsetg_al\nmovzx_rax,al\n", "cmp_rbx,rax\nseta_al\nmovzx_rax,al\n", ">", relational_expr_stub);
        general_recursion(additive_expr_c, "cmp_rbx,rax\nsete_al\nmovzx_rax,al\n", "==", relational_expr_stub);
        general_recursion(additive_expr_c, "cmp_rbx,rax\nsetne_al\nmovzx_rax,al\n", "!=", relational_expr_stub);
    }
    else if(ARMV7L == Architecture)
    {
        arithmetic_recursion(additive_expr_c, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_L\n", "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_LO\n", "<", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_LE\n", "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_LS\n", "<=", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_GE\n", "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_HS\n", ">=", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_G\n", "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_HI\n", ">", relational_expr_stub);
        general_recursion(additive_expr_c, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_EQUAL\n", "==", relational_expr_stub);
        general_recursion(additive_expr_c, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_NE\n", "!=", relational_expr_stub);
    }
    else if(AARCH64 == Architecture)
    {
        arithmetic_recursion(additive_expr_c, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_LT\nSET_X0_TO_0\n", "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_LO\nSET_X0_TO_0\n", "<", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_LE\nSET_X0_TO_0\n", "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_LS\nSET_X0_TO_0\n", "<=", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_GE\nSET_X0_TO_0\n", "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_HS\nSET_X0_TO_0\n", ">=", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_GT\nSET_X0_TO_0\n", "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_HI\nSET_X0_TO_0\n", ">", relational_expr_stub);
        general_recursion(additive_expr_c, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_EQ\nSET_X0_TO_0\n", "==", relational_expr_stub);
        general_recursion(additive_expr_c, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_NE\nSET_X0_TO_0\n", "!=", relational_expr_stub);
    }
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        arithmetic_recursion(additive_expr_c, "rd_a0 rs1_a1 rs2_a0 slt\n", "rd_a0 rs1_a1 rs2_a0 sltu\n", "<", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "rd_a0 rs1_a0 rs2_a1 slt\nrd_a0 rs1_a0 !1 xori\n", "rd_a0 rs1_a0 rs2_a1 sltu\nrd_a0 rs1_a0 !1 xori\n", "<=", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "rd_a0 rs1_a1 rs2_a0 slt\nrd_a0 rs1_a0 !1 xori\n", "rd_a0 rs1_a1 rs2_a0 sltu\nrd_a0 rs1_a0 !1 xori\n", ">=", relational_expr_stub);
        arithmetic_recursion(additive_expr_c, "rd_a0 rs1_a0 rs2_a1 slt\n", "rd_a0 rs1_a0 rs2_a1 sltu\n", ">", relational_expr_stub);
        general_recursion(additive_expr_c, "rd_a0 rs1_a0 rs2_a1 sub\nrd_a0 rs1_a0 !1 sltiu\n", "==", relational_expr_stub);
        general_recursion(additive_expr_c, "rd_a0 rs1_a0 rs2_a1 sub\nrd_a0 rs2_a0 sltu\n", "!=", relational_expr_stub);
    }
}

void relational_expr()
{
    additive_expr_c();
    relational_expr_stub();
}

/*
 * bitwise-expr:
 *         relational-expr
 *         bitwise-expr & bitwise-expr
 *         bitwise-expr && bitwise-expr
 *         bitwise-expr | bitwise-expr
 *         bitwise-expr || bitwise-expr
 *         bitwise-expr ^ bitwise-expr
 */
void bitwise_expr_stub()
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
    {
        general_recursion(relational_expr, "AND R0 R0 R1\n", "&", bitwise_expr_stub);
        general_recursion(relational_expr, "AND R0 R0 R1\n", "&&", bitwise_expr_stub);
        general_recursion(relational_expr, "OR R0 R0 R1\n", "|", bitwise_expr_stub);
        general_recursion(relational_expr, "OR R0 R0 R1\n", "||", bitwise_expr_stub);
        general_recursion(relational_expr, "XOR R0 R0 R1\n", "^", bitwise_expr_stub);
    }
    else if(X86 == Architecture)
    {
        general_recursion(relational_expr, "and_eax,ebx\n", "&", bitwise_expr_stub);
        general_recursion(relational_expr, "and_eax,ebx\n", "&&", bitwise_expr_stub);
        general_recursion(relational_expr, "or_eax,ebx\n", "|", bitwise_expr_stub);
        general_recursion(relational_expr, "or_eax,ebx\n", "||", bitwise_expr_stub);
        general_recursion(relational_expr, "xor_eax,ebx\n", "^", bitwise_expr_stub);
    }
    else if(AMD64 == Architecture)
    {
        general_recursion(relational_expr, "and_rax,rbx\n", "&", bitwise_expr_stub);
        general_recursion(relational_expr, "and_rax,rbx\n", "&&", bitwise_expr_stub);
        general_recursion(relational_expr, "or_rax,rbx\n", "|", bitwise_expr_stub);
        general_recursion(relational_expr, "or_rax,rbx\n", "||", bitwise_expr_stub);
        general_recursion(relational_expr, "xor_rax,rbx\n", "^", bitwise_expr_stub);
    }
    else if(ARMV7L == Architecture)
    {
        general_recursion(relational_expr, "NO_SHIFT R0 R0 AND R1 ARITH2_ALWAYS\n", "&", bitwise_expr_stub);
        general_recursion(relational_expr, "NO_SHIFT R0 R0 AND R1 ARITH2_ALWAYS\n", "&&", bitwise_expr_stub);
        general_recursion(relational_expr, "NO_SHIFT R0 R0 OR R1 AUX_ALWAYS\n", "|", bitwise_expr_stub);
        general_recursion(relational_expr, "NO_SHIFT R0 R0 OR R1 AUX_ALWAYS\n", "||", bitwise_expr_stub);
        general_recursion(relational_expr, "'0' R0 R0 XOR R1 ARITH2_ALWAYS\n", "^", bitwise_expr_stub);
    }
    else if(AARCH64 == Architecture)
    {
        general_recursion(relational_expr, "AND_X0_X1_X0\n", "&", bitwise_expr_stub);
        general_recursion(relational_expr, "AND_X0_X1_X0\n", "&&", bitwise_expr_stub);
        general_recursion(relational_expr, "OR_X0_X1_X0\n", "|", bitwise_expr_stub);
        general_recursion(relational_expr, "OR_X0_X1_X0\n", "||", bitwise_expr_stub);
        general_recursion(relational_expr, "XOR_X0_X1_X0\n", "^", bitwise_expr_stub);
    }
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        general_recursion(relational_expr, "rd_a0 rs1_a1 rs2_a0 and\n", "&", bitwise_expr_stub);
        general_recursion(relational_expr, "rd_a0 rs1_a1 rs2_a0 and\n", "&&", bitwise_expr_stub);
        general_recursion(relational_expr, "rd_a0 rs1_a1 rs2_a0 or\n", "|", bitwise_expr_stub);
        general_recursion(relational_expr, "rd_a0 rs1_a1 rs2_a0 or\n", "||", bitwise_expr_stub);
        general_recursion(relational_expr, "rd_a0 rs1_a1 rs2_a0 xor\n", "^", bitwise_expr_stub);
    }
}


void bitwise_expr()
{
    relational_expr();
    bitwise_expr_stub();
}

/*
 * expression:
 *         bitwise-or-expr
 *         bitwise-or-expr = expression
 */

void primary_expr()
{
    require(NULL != global_token, "Received EOF where primary expression expected\n");
    if(match("&", global_token->s))
    {
        Address_of = TRUE;
        global_token = global_token->next;
        require(NULL != global_token, "Received EOF after & where primary expression expected\n");
    }
    else
    {
        Address_of = FALSE;
    }

    if(match("sizeof", global_token->s)) unary_expr_sizeof();
    else if('-' == global_token->s[0])
    {
        if(X86 == Architecture) emit_out("mov_eax, %0\n");
        else if(AMD64 == Architecture) emit_out("mov_rax, %0\n");
        else if(ARMV7L == Architecture) emit_out("!0 R0 LOADI8_ALWAYS\n");
        else if(AARCH64 == Architecture) emit_out("SET_X0_TO_0\n");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rd_a0 mv\n");

        common_recursion(primary_expr);

        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("NEG R0 R0\n");
        else if(X86 == Architecture) emit_out("sub_ebx,eax\nmov_eax,ebx\n");
        else if(AMD64 == Architecture) emit_out("sub_rbx,rax\nmov_rax,rbx\n");
        else if(ARMV7L == Architecture) emit_out("'0' R0 R0 SUB R1 ARITH2_ALWAYS\n");
        else if(AARCH64 == Architecture) emit_out("SUB_X0_X1_X0\n");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rd_a0 rs1_a1 rs2_a0 sub\n");
    }
    else if('!' == global_token->s[0])
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))  emit_out("LOADI R0 1\n");
        else if(X86 == Architecture) emit_out("mov_eax, %1\n");
        else if(AMD64 == Architecture) emit_out("mov_rax, %1\n");
        else if(ARMV7L == Architecture) emit_out("!1 R0 LOADI8_ALWAYS\n");
        else if(AARCH64 == Architecture) emit_out("SET_X0_TO_1\n");

        common_recursion(postfix_expr);

        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("CMPU R0 R1 R0\nSET.G R0 R0 1\n");
        else if(X86 == Architecture) emit_out("cmp\nseta_al\nmovzx_eax,al\n");
        else if(AMD64 == Architecture) emit_out("cmp_rbx,rax\nseta_al\nmovzx_rax,al\n");
        else if(ARMV7L == Architecture) emit_out("'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_HI\n");
        else if(AARCH64 == Architecture) emit_out("CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_HI\nSET_X0_TO_0\n");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rd_a0 rs1_a0 !1 sltiu\n");
    }
    else if('~' == global_token->s[0])
    {
        common_recursion(postfix_expr);

        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("NOT R0 R0\n");
        else if(X86 == Architecture) emit_out("not_eax\n");
        else if(AMD64 == Architecture) emit_out("not_rax\n");
        else if(ARMV7L == Architecture) emit_out("'0' R0 R0 MVN_ALWAYS\n");
        else if(AARCH64 == Architecture) emit_out("MVN_X0\n");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rd_a0 rs1_a0 not\n");
    }
    else if(global_token->s[0] == '(')
    {
        global_token = global_token->next;
        expression();
        require_match("Error in Primary expression\nDidn't get )\n", ")");
    }
    else if(global_token->s[0] == '\'') primary_expr_char();
    else if(global_token->s[0] == '"') primary_expr_string();
    else if(in_set(global_token->s[0], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_")) primary_expr_variable();
    else if(global_token->s[0] == '*') primary_expr_variable();
    else if(in_set(global_token->s[0], "0123456789"))
    {
        primary_expr_number(global_token->s);
        global_token = global_token->next;
    }
    else primary_expr_failure();
}

char* compound_operation(char* operator, int is_signed)
{
    char* operation = "";
    if(match("+=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            if(is_signed) operation = "ADD R0 R1 R0\n";
            else operation = "ADDU R0 R1 R0\n";
        }
        else if(X86 == Architecture) operation = "add_eax,ebx\n";
        else if(AMD64 == Architecture) operation = "add_rax,rbx\n";
        else if(ARMV7L == Architecture) operation = "'0' R0 R0 ADD R1 ARITH2_ALWAYS\n";
        else if(AARCH64 == Architecture) operation = "ADD_X0_X1_X0\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "rd_a0 rs1_a1 rs2_a0 add\n";
    }
    else if(match("-=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            if(is_signed) operation = "SUB R0 R1 R0\n";
            else operation =  "SUBU R0 R1 R0\n";
        }
        else if(X86 == Architecture) operation = "sub_ebx,eax\nmov_eax,ebx\n";
        else if(AMD64 == Architecture) operation = "sub_rbx,rax\nmov_rax,rbx\n";
        else if(ARMV7L == Architecture) operation = "'0' R0 R0 SUB R1 ARITH2_ALWAYS\n";
        else if(AARCH64 == Architecture) operation = "SUB_X0_X1_X0\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "rd_a0 rs1_a1 rs2_a0 sub\n";
    }
    else if(match("*=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            if(is_signed) operation = "MUL R0 R1 R0\n";
            else operation =  "MULU R0 R1 R0\n";
        }
        else if(X86 == Architecture)
        {
            if(is_signed) operation = "imul_ebx\n";
            else operation = "mul_ebx\n";
        }
        else if(AMD64 == Architecture)
        {
            if(is_signed) operation = "imul_rbx\n";
            else operation = "mul_rbx\n";
        }
        else if(ARMV7L == Architecture) operation = "'9' R0 '0' R1 MULS R0 ARITH2_ALWAYS\n";
        else if(AARCH64 == Architecture) operation = "MUL_X0_X1_X0\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "rd_a0 rs1_a1 rs2_a0 mul\n";
    }
    else if(match("/=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            if(is_signed) operation = "DIV R0 R1 R0\n";
            else operation =  "DIVU R0 R1 R0\n";
        }
        else if(X86 == Architecture)
        {
            if (is_signed) operation = "xchg_ebx,eax\ncdq\nidiv_ebx\n";
            else operation = "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\n";
        }
        else if(AMD64 == Architecture)
        {
            if(is_signed) operation = "xchg_rbx,rax\ncqo\nidiv_rbx\n";
            else operation = "xchg_rbx,rax\nmov_rdx, %0\ndiv_rbx\n";
        }
        else if(ARMV7L == Architecture)
        {
            if(is_signed) operation = "{LR} PUSH_ALWAYS\n^~divides CALL_ALWAYS\n{LR} POP_ALWAYS\n";
            else operation = "{LR} PUSH_ALWAYS\n^~divide CALL_ALWAYS\n{LR} POP_ALWAYS\n";
        }
        else if(AARCH64 == Architecture)
        {
            if(is_signed) operation = "SDIV_X0_X1_X0\n";
            else operation = "UDIV_X0_X1_X0\n";
        }
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
        {
            if(is_signed) operation = "rd_a0 rs1_a1 rs2_a0 div\n";
            else operation = "rd_a0 rs1_a1 rs2_a0 divu\n";
        }
    }
    else if(match("%=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            if(is_signed) operation = "MOD R0 R1 R0\n";
            else operation = "MODU R0 R1 R0\n";
        }
        else if(X86 == Architecture)
        {
            if(is_signed) operation = "xchg_ebx,eax\ncdq\nidiv_ebx\nmov_eax,edx\n";
            else operation = "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\nmov_eax,edx\n";
        }
        else if(AMD64 == Architecture)
        {
            if(is_signed) operation = "xchg_rbx,rax\ncqo\nidiv_rbx\nmov_rax,rdx\n";
            else operation = "xchg_rbx,rax\nmov_rdx, %0\ndiv_rbx\nmov_rax,rdx\n";
        }
        else if(ARMV7L == Architecture)
        {
            if(is_signed) operation = "{LR} PUSH_ALWAYS\n^~moduluss CALL_ALWAYS\n{LR} POP_ALWAYS\n";
            else operation = "{LR} PUSH_ALWAYS\n^~modulus CALL_ALWAYS\n{LR} POP_ALWAYS\n";
        }
        else if(AARCH64 == Architecture)
        {
            if(is_signed) operation = "SDIV_X2_X1_X0\nMSUB_X0_X0_X2_X1\n";
            else operation = "UDIV_X2_X1_X0\nMSUB_X0_X0_X2_X1\n";
        }
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
        {
            if(is_signed) operation = "rd_a0 rs1_a1 rs2_a0 rem\n";
            else operation = "rd_a0 rs1_a1 rs2_a0 remu\n";
        }
    }
    else if(match("<<=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            if(is_signed) operation = "SAL R0 R1 R0\n";
            else operation = "SL0 R0 R1 R0\n";
        }
        else if(X86 == Architecture)
        {
            if(is_signed) operation = "mov_ecx,eax\nmov_eax,ebx\nsal_eax,cl\n";
            else operation = "mov_ecx,eax\nmov_eax,ebx\nshl_eax,cl\n";
        }
        else if(AMD64 == Architecture)
        {
            if(is_signed) operation = "mov_rcx,rax\nmov_rax,rbx\nsal_rax,cl\n";
            else operation = "mov_rcx,rax\nmov_rax,rbx\nshl_rax,cl\n";
        }
        else if(ARMV7L == Architecture) operation = "LEFT R1 R0 R0 SHIFT AUX_ALWAYS\n";
        else if(AARCH64 == Architecture) operation = "LSHIFT_X0_X1_X0\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "rd_a0 rs1_a1 rs2_a0 sll\n";
    }
    else if(match(">>=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            if(is_signed) operation = "SAR R0 R1 R0\n";
            else operation = "SR0 R0 R1 R0\n";
        }
        else if(X86 == Architecture)
        {
            if(is_signed) operation = "mov_ecx,eax\nmov_eax,ebx\nsar_eax,cl\n";
            else operation = "mov_ecx,eax\nmov_eax,ebx\nshr_eax,cl\n";
        }
        else if(AMD64 == Architecture)
        {
            if(is_signed) operation = "mov_rcx,rax\nmov_rax,rbx\nsar_rax,cl\n";
            else operation = "mov_rcx,rax\nmov_rax,rbx\nshr_rax,cl\n";
        }
        else if(ARMV7L == Architecture)
        {
            if(is_signed) operation = "ARITH_RIGHT R1 R0 R0 SHIFT AUX_ALWAYS\n";
            else operation = "RIGHT R1 R0 R0 SHIFT AUX_ALWAYS\n";
        }
        else if(AARCH64 == Architecture)
        {
            if(is_signed) operation = "ARITH_RSHIFT_X0_X1_X0\n";
            else operation = "LOGICAL_RSHIFT_X0_X1_X0\n";
        }
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
        {
            if(is_signed) operation = "rd_a0 rs1_a1 rs2_a0 sra\n";
            else operation = "rd_a0 rs1_a1 rs2_a0 srl\n";
        }
    }
    else if(match("&=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) operation = "AND R0 R0 R1\n";
        else if(X86 == Architecture) operation = "and_eax,ebx\n";
        else if(AMD64 == Architecture) operation = "and_rax,rbx\n";
        else if(ARMV7L == Architecture) operation = "NO_SHIFT R0 R0 AND R1 ARITH2_ALWAYS\n";
        else if(AARCH64 == Architecture) operation = "AND_X0_X1_X0\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "rd_a0 rs1_a1 rs2_a0 and\n";
    }
    else if(match("^=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) operation = "XOR R0 R0 R1\n";
        else if(X86 == Architecture) operation = "xor_eax,ebx\n";
        else if(AMD64 == Architecture) operation = "xor_rax,rbx\n";
        else if(ARMV7L == Architecture) operation = "'0' R0 R0 XOR R1 ARITH2_ALWAYS\n";
        else if(AARCH64 == Architecture) operation = "XOR_X0_X1_X0\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "rd_a0 rs1_a1 rs2_a0 xor\n";
    }
    else if(match("|=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) operation = "OR R0 R0 R1\n";
        else if(X86 == Architecture) operation = "or_eax,ebx\n";
        else if(AMD64 == Architecture) operation = "or_rax,rbx\n";
        else if(ARMV7L == Architecture) operation = "NO_SHIFT R0 R0 OR R1 AUX_ALWAYS\n";
        else if(AARCH64 == Architecture) operation = "OR_X0_X1_X0\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "rd_a0 rs1_a1 rs2_a0 or\n";
    }
    else
    {
        fputs("Found illegal compound assignment operator: ", stderr);
        fputs(operator, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }
    return operation;
}


void expression()
{
    bitwise_expr();
    if(match("=", global_token->s))
    {
        char* store = "";
        if(match("]", global_token->prev->s))
        {
            store = store_value(current_target->type->size);
        }
        else
        {
            store = store_value(current_target->size);
        }

        common_recursion(expression);
        emit_out(store);
        current_target = integer;
    }
    else if(is_compound_assignment(global_token->s))
    {
        maybe_bootstrap_error("compound operator");
        char* push = "";
        char* load = "";
        char* operation = "";
        char* pop = "";
        char* store = "";
        struct type* last_type = current_target;

        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) push = "PUSHR R1 R15\n";
        else if(X86 == Architecture) push = "push_ebx\n";
        else if(AMD64 == Architecture) push = "push_rbx\n";
        else if(ARMV7L == Architecture) push = "{R1} PUSH_ALWAYS\n";
        else if(AARCH64 == Architecture) push = "PUSH_X1\n";
        else if(RISCV32 == Architecture) push = "rs1_sp rs2_a1 @-4 sw\n";
        else if(RISCV64 == Architecture) push = "rs1_sp rs2_a1 @-8 sd\n";

        if(!match("]", global_token->prev->s) || !match("char*", current_target->name))
        {
            if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) load = "LOAD R1 R1 0\n";
            else if(X86 == Architecture) load = "mov_ebx,[ebx]\n";
            else if(AMD64 == Architecture) load = "mov_rbx,[rbx]\n";
            else if(ARMV7L == Architecture) load = "!0 R1 LOAD32 R1 MEMORY\n";
            else if(AARCH64 == Architecture) load = "DEREF_X1\n";
            else if(RISCV32 == Architecture) load = "rd_a1 rs1_a1 lw\n";
            else if(RISCV64 == Architecture) load = "rd_a1 rs1_a1 ld\n";
        }
        else
        {
            if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) load = "LOAD8 R1 R1 0\n";
            else if(X86 == Architecture) load = "movsx_ebx,BYTE_PTR_[ebx]\n";
            else if(AMD64 == Architecture) load = "movsx_rbx,BYTE_PTR_[rbx]\n";
            else if(ARMV7L == Architecture) load = "LOADU8 R1 LOAD R1 MEMORY\n";
            else if(AARCH64 == Architecture) load = "DEREF_X1_BYTE\n";
            else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) load = "rd_a1 rs1_a1 lbu\n";
        }

        char *operator = global_token->s;

        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) pop = "POPR R1 R15\n";
        else if(X86 == Architecture) pop = "pop_ebx\n";
        else if(AMD64 == Architecture) pop = "pop_rbx\n";
        else if(ARMV7L == Architecture) pop = "{R1} POP_ALWAYS\n";
        else if(AARCH64 == Architecture) pop = "POP_X1\n";
        else if(RISCV32 == Architecture) pop = "rd_a1 rs1_sp !-4 lw\n";
        else if(RISCV64 == Architecture) pop = "rd_a1 rs1_sp !-8 ld\n";

        if(match("]", global_token->prev->s))
        {
            store = store_value(current_target->type->size);
        }
        else
        {
            store = store_value(current_target->size);
        }

        common_recursion(expression);
        current_target = promote_type(current_target, last_type);
        emit_out(push);
        emit_out(load);
        operation = compound_operation(operator, current_target->is_signed);
        emit_out(operation);
        emit_out(pop);
        emit_out(store);
        current_target = integer;
    }
}


int iskeywordp(char* s)
{
    if(match("auto", s)) return TRUE;
    if(match("break", s)) return TRUE;
    if(match("case", s)) return TRUE;
    if(match("char", s)) return TRUE;
    if(match("const", s)) return TRUE;
    if(match("continue", s)) return TRUE;
    if(match("default", s)) return TRUE;
    if(match("do", s)) return TRUE;
    if(match("double", s)) return TRUE;
    if(match("else", s)) return TRUE;
    if(match("enum", s)) return TRUE;
    if(match("extern", s)) return TRUE;
    if(match("float", s)) return TRUE;
    if(match("for", s)) return TRUE;
    if(match("goto", s)) return TRUE;
    if(match("if", s)) return TRUE;
    if(match("int", s)) return TRUE;
    if(match("long", s)) return TRUE;
    if(match("register", s)) return TRUE;
    if(match("return", s)) return TRUE;
    if(match("short", s)) return TRUE;
    if(match("signed", s)) return TRUE;
    if(match("sizeof", s)) return TRUE;
    if(match("static", s)) return TRUE;
    if(match("struct", s)) return TRUE;
    if(match("switch", s)) return TRUE;
    if(match("typedef", s)) return TRUE;
    if(match("union", s)) return TRUE;
    if(match("unsigned", s)) return TRUE;
    if(match("void", s)) return TRUE;
    if(match("volatile", s)) return TRUE;
    if(match("while", s)) return TRUE;
    return FALSE;
}

/* Similar to integer division a / b but rounds up */
unsigned ceil_div(unsigned a, unsigned b)
{
    return (a + b - 1) / b;
}

/* Process local variable */
void collect_local()
{
    if(NULL != break_target_func)
    {
        fputs("Local variable initialized inside of loop in file: ", stderr);
        line_error();
        fputs("\nMove the variable outside of the loop to resolve\n", stderr);
        fputs("Otherwise the binary will segfault while running\n", stderr);
        exit(EXIT_FAILURE);
    }
    struct type* type_size = type_name();
    require(NULL != global_token, "Received EOF while collecting locals\n");
    require(!in_set(global_token->s[0], "[{(<=>)}]|&!^%;:'\""), "forbidden character in local variable name\n");
    require(!iskeywordp(global_token->s), "You are not allowed to use a keyword as a local variable name\n");
    require(NULL != type_size, "Must have non-null type\n");
    struct token_list* a = sym_declare(global_token->s, type_size, function->locals);
    if(match("main", function->s) && (NULL == function->locals))
    {
        if(KNIGHT_NATIVE == Architecture) a->depth = register_size;
        else if(KNIGHT_POSIX == Architecture) a->depth = 20;
        else if(X86 == Architecture) a->depth = -20;
        else if(AMD64 == Architecture) a->depth = -40;
        else if(ARMV7L == Architecture) a->depth = 16;
        else if(AARCH64 == Architecture) a->depth = 32; /* argc, argv, envp and the local (8 bytes each) */
        else if(RISCV32 == Architecture) a->depth = -16;
        else if(RISCV64 == Architecture) a->depth = -32;
    }
    else if((NULL == function->arguments) && (NULL == function->locals))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = register_size;
        else if(X86 == Architecture) a->depth = -8;
        else if(AMD64 == Architecture) a->depth = -16;
        else if(ARMV7L == Architecture) a->depth = 8;
        else if(AARCH64 == Architecture) a->depth = register_size;
        else if(RISCV32 == Architecture) a->depth = -4;
        else if(RISCV64 == Architecture) a->depth = -8;
    }
    else if(NULL == function->locals)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = function->arguments->depth + 8;
        else if(X86 == Architecture) a->depth = function->arguments->depth - 8;
        else if(AMD64 == Architecture) a->depth = function->arguments->depth - 16;
        else if(ARMV7L == Architecture) a->depth = function->arguments->depth + 8;
        else if(AARCH64 == Architecture) a->depth = function->arguments->depth + register_size;
        else if(RISCV32 == Architecture) a->depth = function->arguments->depth - 4;
        else if(RISCV64 == Architecture) a->depth = function->arguments->depth - 8;
    }
    else
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = function->locals->depth + register_size;
        else if(X86 == Architecture) a->depth = function->locals->depth - register_size;
        else if(AMD64 == Architecture) a->depth = function->locals->depth - register_size;
        else if(ARMV7L == Architecture) a->depth = function->locals->depth + register_size;
        else if(AARCH64 == Architecture) a->depth = function->locals->depth + register_size;
        else if(RISCV32 == Architecture) a->depth = function->locals->depth - register_size;
        else if(RISCV64 == Architecture) a->depth = function->locals->depth - register_size;
    }

    /* Adjust the depth of local structs. When stack grows downwards, we want them to 
       start at the bottom of allocated space. */
    unsigned struct_depth_adjustment = (ceil_div(a->type->size, register_size) - 1) * register_size;
    if(KNIGHT_POSIX == Architecture) a->depth = a->depth + struct_depth_adjustment;
    else if(KNIGHT_NATIVE == Architecture) a->depth = a->depth + struct_depth_adjustment;
    else if(X86 == Architecture) a->depth = a->depth - struct_depth_adjustment;
    else if(AMD64 == Architecture) a->depth = a->depth - struct_depth_adjustment;
    else if(ARMV7L == Architecture) a->depth = a->depth + struct_depth_adjustment;
    else if(AARCH64 == Architecture) a->depth = a->depth + struct_depth_adjustment;
    else if(RISCV32 == Architecture) a->depth = a->depth - struct_depth_adjustment;
    else if(RISCV64 == Architecture) a->depth = a->depth - struct_depth_adjustment;

    function->locals = a;

    emit_out("# Defining local ");
    emit_out(global_token->s);
    emit_out("\n");

    global_token = global_token->next;
    require(NULL != global_token, "incomplete local missing name\n");

    if(match("=", global_token->s))
    {
        global_token = global_token->next;
        require(NULL != global_token, "incomplete local assignment\n");
        expression();
    }

    require_match("ERROR in collect_local\nMissing ;\n", ";");

    unsigned i = (a->type->size + register_size - 1) / register_size;
    while(i != 0)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R0 R15\t#");
        else if(X86 == Architecture) emit_out("push_eax\t#");
        else if(AMD64 == Architecture) emit_out("push_rax\t#");
        else if(ARMV7L == Architecture) emit_out("{R0} PUSH_ALWAYS\t#");
        else if(AARCH64 == Architecture) emit_out("PUSH_X0\t#");
        else if(RISCV32 == Architecture) emit_out("rd_sp rs1_sp !-4 addi\nrs1_sp rs2_a0 sw\t#");
        else if(RISCV64 == Architecture) emit_out("rd_sp rs1_sp !-8 addi\nrs1_sp rs2_a0 sd\t#");
        emit_out(a->s);
        emit_out("\n");
        i = i - 1;
    }
}

void statement();

/* Evaluate if statements */
void process_if()
{
    char* number_string = int2str(current_count, 10, TRUE);
    current_count = current_count + 1;

    emit_out("# IF_");
    uniqueID_out(function->s, number_string);

    global_token = global_token->next;
    require_match("ERROR in process_if\nMISSING (\n", "(");
    expression();

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP.Z R0 @ELSE_");
    else if(X86 == Architecture) emit_out("test_eax,eax\nje %ELSE_");
    else if(AMD64 == Architecture) emit_out("test_rax,rax\nje %ELSE_");
    else if(ARMV7L == Architecture) emit_out("!0 CMPI8 R0 IMM_ALWAYS\n^~ELSE_");
    else if(AARCH64 == Architecture) emit_out("CBNZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&ELSE_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rs1_a0 @8 bnez\n$ELSE_");

    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_EQUAL\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("jal\n");

    require_match("ERROR in process_if\nMISSING )\n", ")");
    statement();
    require(NULL != global_token, "Reached EOF inside of function\n");

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @_END_IF_");
    else if(X86 == Architecture) emit_out("jmp %_END_IF_");
    else if(AMD64 == Architecture) emit_out("jmp %_END_IF_");
    else if(ARMV7L == Architecture) emit_out("^~_END_IF_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&_END_IF_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$_END_IF_");

    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("jal\n");

    emit_out(":ELSE_");
    uniqueID_out(function->s, number_string);

    if(match("else", global_token->s))
    {
        global_token = global_token->next;
        require(NULL != global_token, "Received EOF where an else statement expected\n");
        statement();
        require(NULL != global_token, "Reached EOF inside of function\n");
    }
    emit_out(":_END_IF_");
    uniqueID_out(function->s, number_string);
}

void process_case()
{
process_case_iter:
    if(match("case", global_token->s)) return;
    if(match(":default", global_token->s)) return;

    if(match("break", global_token->s))
    {
        statement();
    }
    else
    {
        statement();
        goto process_case_iter;
    }
}

void process_switch()
{
    maybe_bootstrap_error("switch/case statements");
    struct token_list* nested_locals = break_frame;
    char* nested_break_head = break_target_head;
    char* nested_break_func = break_target_func;
    char* nested_break_num = break_target_num;
    char* nested_continue_head = continue_target_head;

    char* number_string = int2str(current_count, 10, TRUE);
    current_count = current_count + 1;

    break_target_head = "_SWITCH_END_";
    continue_target_head = NULL; /* don't allow continue in switch statements */
    break_target_num = number_string;
    break_frame = function->locals;
    break_target_func = function->s;

    emit_out("# switch_");
    uniqueID_out(function->s, number_string);

    /* get what we are casing on */
    global_token = global_token->next;
    require_match("ERROR in process_switch\nMISSING (\n", "(");
    expression();
    require_match("ERROR in process_switch\nMISSING )\n", ")");

    /* Put the value in R1 as it is currently in R0 */
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("MOVE R1 R0\n");
    else if(X86 == Architecture) emit_out("mov_ebx,eax\n");
    else if(AMD64 == Architecture) emit_out("push_rax\npop_rbx\n");
    else if(ARMV7L == Architecture) emit_out("'0' R1 R0 NO_SHIFT MOVE_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("SET_X1_FROM_X0\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rd_a1 rs1_a0 mv\n");

    /* Jump to the switch table */
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @_SWITCH_TABLE_");
    else if(X86 == Architecture) emit_out("jmp %_SWITCH_TABLE_");
    else if(AMD64 == Architecture) emit_out("jmp %_SWITCH_TABLE_");
    else if(ARMV7L == Architecture) emit_out("^~_SWITCH_TABLE_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&_SWITCH_TABLE_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$_SWITCH_TABLE_");

    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("jal\n");

    /* must be switch (exp) {$STATEMENTS}; form */
    require_match("ERROR in process_switch\nMISSING {\n", "{");
    struct case_list* backtrack = NULL;
process_switch_iter:
    if(match("case", global_token->s))
    {
        global_token = global_token->next;
        if(':' == global_token->s[0])
        {
            struct case_list* c = calloc(1, sizeof(struct case_list));
            c->next = backtrack;
            c->value = global_token->s + 1;
            backtrack = c;
            emit_out(":_SWITCH_CASE_");
            emit_out(c->value);
            emit_out("_");
            uniqueID_out(function->s, number_string);
            global_token = global_token->next;
            process_case();
        }
        else line_error();
        goto process_switch_iter;
    }
    else if(match(":default", global_token->s))
    { /* because of how M2-Planet treats labels */
        global_token = global_token->next;
        emit_out(":_SWITCH_DEFAULT_");
        uniqueID_out(function->s, number_string);

        /* collect statements until } */
        while(!match("}", global_token->s))
        {
            statement();
        }

        /* jump over the switch table */
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @_SWITCH_END_");
        else if(X86 == Architecture) emit_out("jmp %_SWITCH_END_");
        else if(AMD64 == Architecture) emit_out("jmp %_SWITCH_END_");
        else if(ARMV7L == Architecture) emit_out("^~_SWITCH_END_");
        else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&_SWITCH_END_");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$_SWITCH_END_");

        uniqueID_out(function->s, number_string);
        if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
        else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("jal\n");
    }

    /* Switch statements must end with } */
    require_match("ERROR in process_switch\nMISSING }\n", "}");

    /* create the table */
    emit_out(":_SWITCH_TABLE_");
    uniqueID_out(function->s, number_string);

    struct case_list* hold;
    while(NULL != backtrack)
    {
        /* put case value in R0 as the switch (value) is in R1 */
        primary_expr_number(backtrack->value);
        hold = backtrack->next;

        /* compare R0 and R1 and jump to case if equal */
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("CMPU R0 R0 R1\nJUMP.E R0 @_SWITCH_CASE_");
        else if(X86 == Architecture) emit_out("cmp\nje %_SWITCH_CASE_");
        else if(AMD64 == Architecture) emit_out("cmp_rbx,rax\nje %_SWITCH_CASE_");
        else if(ARMV7L == Architecture) emit_out("'0' R0 CMP R1 AUX_ALWAYS\n^~_SWITCH_CASE_");
        else if(AARCH64 == Architecture) emit_out("CMP_X1_X0\nSKIP_32_DATA\n&_SWITCH_CASE_");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rd_a0 rs1_a0 rs2_a1 sub\nrs1_a0 @8 bnez\n$_SWITCH_CASE_");

        emit_out(backtrack->value);
        emit_out("_");
        uniqueID_out(function->s, number_string);
        if(ARMV7L == Architecture) emit_out(" JUMP_EQUAL\n");
        else if(AARCH64 == Architecture) emit_out("\nSKIP_INST_NE\nBR_X16\n");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("jal\n");

        free(backtrack);
        backtrack = hold;
    }

    /* Default to :default */
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @_SWITCH_DEFAULT_");
    else if(X86 == Architecture) emit_out("jmp %_SWITCH_DEFAULT_");
    else if(AMD64 == Architecture) emit_out("jmp %_SWITCH_DEFAULT_");
    else if(ARMV7L == Architecture) emit_out("^~_SWITCH_DEFAULT_");
    else if(AARCH64 == Architecture) emit_out("SKIP_32_DATA\n&_SWITCH_DEFAULT_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$_SWITCH_DEFAULT_");

    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("jal\n");

    /* put the exit of the switch */
    emit_out(":_SWITCH_END_");
    uniqueID_out(function->s, number_string);

    break_target_head = nested_break_head;
    break_target_func = nested_break_func;
    break_target_num = nested_break_num;
    continue_target_head = nested_continue_head;
    break_frame = nested_locals;
}

void process_for()
{
    struct token_list* nested_locals = break_frame;
    char* nested_break_head = break_target_head;
    char* nested_break_func = break_target_func;
    char* nested_break_num = break_target_num;
    char* nested_continue_head = continue_target_head;

    char* number_string = int2str(current_count, 10, TRUE);
    current_count = current_count + 1;

    break_target_head = "FOR_END_";
    continue_target_head = "FOR_ITER_";
    break_target_num = number_string;
    break_frame = function->locals;
    break_target_func = function->s;

    emit_out("# FOR_initialization_");
    uniqueID_out(function->s, number_string);

    global_token = global_token->next;

    require_match("ERROR in process_for\nMISSING (\n", "(");
    if(!match(";",global_token->s))
    {
        expression();
    }

    emit_out(":FOR_");
    uniqueID_out(function->s, number_string);

    require_match("ERROR in process_for\nMISSING ;1\n", ";");
    expression();

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP.Z R0 @FOR_END_");
    else if(X86 == Architecture) emit_out("test_eax,eax\nje %FOR_END_");
    else if(AMD64 == Architecture) emit_out("test_rax,rax\nje %FOR_END_");
    else if(ARMV7L == Architecture) emit_out("!0 CMPI8 R0 IMM_ALWAYS\n^~FOR_END_");
    else if(AARCH64 == Architecture) emit_out("CBNZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_END_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rs1_a0 @8 bnez\n$FOR_END_");
    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_EQUAL\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("jal\n");

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @FOR_THEN_");
    else if(X86 == Architecture) emit_out("jmp %FOR_THEN_");
    else if(AMD64 == Architecture) emit_out("jmp %FOR_THEN_");
    else if(ARMV7L == Architecture) emit_out("^~FOR_THEN_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_THEN_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$FOR_THEN_");
    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("jal\n");

    emit_out(":FOR_ITER_");
    uniqueID_out(function->s, number_string);

    require_match("ERROR in process_for\nMISSING ;2\n", ";");
    expression();

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @FOR_");
    else if(X86 == Architecture) emit_out("jmp %FOR_");
    else if(AMD64 == Architecture) emit_out("jmp %FOR_");
    else if(ARMV7L == Architecture) emit_out("^~FOR_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$FOR_");
    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("jal\n");

    emit_out(":FOR_THEN_");
    uniqueID_out(function->s, number_string);

    require_match("ERROR in process_for\nMISSING )\n", ")");
    statement();
    require(NULL != global_token, "Reached EOF inside of function\n");

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @FOR_ITER_");
    else if(X86 == Architecture) emit_out("jmp %FOR_ITER_");
    else if(AMD64 == Architecture) emit_out("jmp %FOR_ITER_");
    else if(ARMV7L == Architecture) emit_out("^~FOR_ITER_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_ITER_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$FOR_ITER_");
    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("jal\n");

    emit_out(":FOR_END_");
    uniqueID_out(function->s, number_string);

    break_target_head = nested_break_head;
    break_target_func = nested_break_func;
    break_target_num = nested_break_num;
    continue_target_head = nested_continue_head;
    break_frame = nested_locals;
}

/* Process Assembly statements */
void process_asm()
{
    global_token = global_token->next;
    require_match("ERROR in process_asm\nMISSING (\n", "(");
    while('"' == global_token->s[0])
    {
        emit_out((global_token->s + 1));
        emit_out("\n");
        global_token = global_token->next;
        require(NULL != global_token, "Received EOF inside asm statement\n");
    }
    require_match("ERROR in process_asm\nMISSING )\n", ")");
    require_match("ERROR in process_asm\nMISSING ;\n", ";");
}

/* Process do while loops */
void process_do()
{
    struct token_list* nested_locals = break_frame;
    char* nested_break_head = break_target_head;
    char* nested_break_func = break_target_func;
    char* nested_break_num = break_target_num;
    char* nested_continue_head = continue_target_head;

    char* number_string = int2str(current_count, 10, TRUE);
    current_count = current_count + 1;

    break_target_head = "DO_END_";
    continue_target_head = "DO_TEST_";
    break_target_num = number_string;
    break_frame = function->locals;
    break_target_func = function->s;

    emit_out(":DO_");
    uniqueID_out(function->s, number_string);

    global_token = global_token->next;
    require(NULL != global_token, "Received EOF where do statement is expected\n");
    statement();
    require(NULL != global_token, "Reached EOF inside of function\n");

    emit_out(":DO_TEST_");
    uniqueID_out(function->s, number_string);

    require_match("ERROR in process_do\nMISSING while\n", "while");
    require_match("ERROR in process_do\nMISSING (\n", "(");
    expression();
    require_match("ERROR in process_do\nMISSING )\n", ")");
    require_match("ERROR in process_do\nMISSING ;\n", ";");

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP.NZ R0 @DO_");
    else if(X86 == Architecture) emit_out("test_eax,eax\njne %DO_");
    else if(AMD64 == Architecture) emit_out("test_rax,rax\njne %DO_");
    else if(ARMV7L == Architecture) emit_out("!0 CMPI8 R0 IMM_ALWAYS\n^~DO_");
    else if(AARCH64 == Architecture) emit_out("CBZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&DO_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rs1_a0 @DO_END_");
    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_NE\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        emit_out("beqz\n$DO_");
        uniqueID_out(function->s, number_string);
        emit_out("jal\n");
    }

    emit_out(":DO_END_");
    uniqueID_out(function->s, number_string);

    break_frame = nested_locals;
    break_target_head = nested_break_head;
    break_target_func = nested_break_func;
    break_target_num = nested_break_num;
    continue_target_head = nested_continue_head;
}


/* Process while loops */
void process_while()
{
    struct token_list* nested_locals = break_frame;
    char* nested_break_head = break_target_head;
    char* nested_break_func = break_target_func;
    char* nested_break_num = break_target_num;
    char* nested_continue_head = continue_target_head;

    char* number_string = int2str(current_count, 10, TRUE);
    current_count = current_count + 1;

    break_target_head = "END_WHILE_";
    continue_target_head = "WHILE_";
    break_target_num = number_string;
    break_frame = function->locals;
    break_target_func = function->s;

    emit_out(":WHILE_");
    uniqueID_out(function->s, number_string);

    global_token = global_token->next;
    require_match("ERROR in process_while\nMISSING (\n", "(");
    expression();

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP.Z R0 @END_WHILE_");
    else if(X86 == Architecture) emit_out("test_eax,eax\nje %END_WHILE_");
    else if(AMD64 == Architecture) emit_out("test_rax,rax\nje %END_WHILE_");
    else if(ARMV7L == Architecture) emit_out("!0 CMPI8 R0 IMM_ALWAYS\n^~END_WHILE_");
    else if(AARCH64 == Architecture) emit_out("CBNZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&END_WHILE_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("rs1_a0 @8 bnez\n$END_WHILE_");
    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_EQUAL\t");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("jal\n");
    emit_out("# THEN_while_");
    uniqueID_out(function->s, number_string);

    require_match("ERROR in process_while\nMISSING )\n", ")");
    statement();
    require(NULL != global_token, "Reached EOF inside of function\n");

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @WHILE_");
    else if(X86 == Architecture) emit_out("jmp %WHILE_");
    else if(AMD64 == Architecture) emit_out("jmp %WHILE_");
    else if(ARMV7L == Architecture) emit_out("^~WHILE_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&WHILE_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$WHILE_");
    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("jal\n");
    emit_out(":END_WHILE_");
    uniqueID_out(function->s, number_string);

    break_target_head = nested_break_head;
    break_target_func = nested_break_func;
    break_target_num = nested_break_num;
    continue_target_head = nested_continue_head;
    break_frame = nested_locals;
}

/* Ensure that functions return */
void return_result()
{
    global_token = global_token->next;
    require(NULL != global_token, "Incomplete return statement received\n");
    if(global_token->s[0] != ';') expression();

    require_match("ERROR in return_result\nMISSING ;\n", ";");

    struct token_list* i;
    unsigned size_local_var;
    for(i = function->locals; NULL != i; i = i->next)
    {
        size_local_var = ceil_div(i->type->size, register_size);
        while(size_local_var != 0)
        {
            if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# _return_result_locals\n");
            else if(X86 == Architecture) emit_out("pop_ebx\t# _return_result_locals\n");
            else if(AMD64 == Architecture) emit_out("pop_rbx\t# _return_result_locals\n");
            else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# _return_result_locals\n");
            else if(AARCH64 == Architecture) emit_out("POP_X1\t# _return_result_locals\n");
            else if(RISCV32 == Architecture) emit_out("rd_a1 rs1_sp lw  # _return_result_locals\nrd_sp rs1_sp !4 addi\n");
            else if(RISCV64 == Architecture) emit_out("rd_a1 rs1_sp ld  # _return_result_locals\nrd_sp rs1_sp !8 addi\n");
            size_local_var = size_local_var - 1;
        }
    }

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("RET R15\n");
    else if(X86 == Architecture) emit_out("ret\n");
    else if(AMD64 == Architecture) emit_out("ret\n");
    else if(ARMV7L == Architecture) emit_out("'1' LR RETURN\n");
    else if(AARCH64 == Architecture) emit_out("RETURN\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("ret\n");
}

void process_break()
{
    if(NULL == break_target_head)
    {
        line_error();
        fputs("Not inside of a loop or case statement\n", stderr);
        exit(EXIT_FAILURE);
    }
    struct token_list* i = function->locals;
    while(i != break_frame)
    {
        if(NULL == i) break;
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# break_cleanup_locals\n");
        else if(X86 == Architecture) emit_out("pop_ebx\t# break_cleanup_locals\n");
        else if(AMD64 == Architecture) emit_out("pop_rbx\t# break_cleanup_locals\n");
        else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# break_cleanup_locals\n");
        else if(AARCH64 == Architecture) emit_out("POP_X1\t# break_cleanup_locals\n");
        else if(RISCV32 == Architecture) emit_out("rd_a1 rs1_sp lw\t# break_cleanup_locals\nrd_sp rs1_sp !4 addi\n");
        else if(RISCV64 == Architecture) emit_out("rd_a1 rs1_sp ld\t# break_cleanup_locals\nrd_sp rs1_sp !8 addi\n");
        i = i->next;
    }
    global_token = global_token->next;

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @");
    else if(X86 == Architecture) emit_out("jmp %");
    else if(AMD64 == Architecture) emit_out("jmp %");
    else if(ARMV7L == Architecture) emit_out("^~");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$");

    emit_out(break_target_head);
    emit_out(break_target_func);
    emit_out("_");
    emit_out(break_target_num);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" jal");
    emit_out("\n");
    require_match("ERROR in break statement\nMissing ;\n", ";");
}

void process_continue()
{
    if(NULL == continue_target_head)
    {
        line_error();
        fputs("Not inside of a loop\n", stderr);
        exit(EXIT_FAILURE);
    }
    global_token = global_token->next;

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @");
    else if(X86 == Architecture) emit_out("jmp %");
    else if(AMD64 == Architecture) emit_out("jmp %");
    else if(ARMV7L == Architecture) emit_out("^~");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$");

    emit_out(continue_target_head);
    emit_out(break_target_func);
    emit_out("_");
    emit_out(break_target_num);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" jal");
    emit_out("\n");
    require_match("ERROR in continue statement\nMissing ;\n", ";");
}

void recursive_statement()
{
    global_token = global_token->next;
    require(NULL != global_token, "Received EOF in recursive statement\n");
    struct token_list* frame = function->locals;

    while(!match("}", global_token->s))
    {
        statement();
        require(NULL != global_token, "Received EOF in recursive statement prior to }\n");
    }
    global_token = global_token->next;

    /* Clean up any locals added */

    if(((X86 == Architecture) && !match("ret\n", output_list->s)) ||
       ((AMD64 == Architecture) && !match("ret\n", output_list->s)) ||
       (((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) && !match("RET R15\n", output_list->s)) ||
       ((ARMV7L == Architecture) && !match("'1' LR RETURN\n", output_list->s)) ||
       ((AARCH64 == Architecture) && !match("RETURN\n", output_list->s)) ||
       (((RISCV32 == Architecture) || (RISCV64 == Architecture)) && !match("ret\n", output_list->s)))
    {
        struct token_list* i;
        for(i = function->locals; frame != i; i = i->next)
        {
            if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# _recursive_statement_locals\n");
            else if(X86 == Architecture) emit_out( "pop_ebx\t# _recursive_statement_locals\n");
            else if(AMD64 == Architecture) emit_out("pop_rbx\t# _recursive_statement_locals\n");
            else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# _recursive_statement_locals\n");
            else if(AARCH64 == Architecture) emit_out("POP_X1\t# _recursive_statement_locals\n");
            else if(RISCV32 == Architecture) emit_out("rd_a1 rs1_sp lw\t# _recursive_statement_locals\nrd_sp rs1_sp !4 addi\n");
            else if(RISCV64 == Architecture) emit_out("rd_a1 rs1_sp ld\t# _recursive_statement_locals\nrd_sp rs1_sp !8 addi\n");
        }
    }
    function->locals = frame;
}

/*
 * statement:
 *     { statement-list-opt }
 *     type-name identifier ;
 *     type-name identifier = expression;
 *     if ( expression ) statement
 *     if ( expression ) statement else statement
 *     do statement while ( expression ) ;
 *     while ( expression ) statement
 *     for ( expression ; expression ; expression ) statement
 *     asm ( "assembly" ... "assembly" ) ;
 *     goto label ;
 *     label:
 *     return ;
 *     break ;
 *     expr ;
 */

struct type* lookup_type(char* s, struct type* start);
void statement()
{
    require(NULL != global_token, "expected a C statement but received EOF\n");
    /* Always an integer until told otherwise */
    current_target = integer;

    if(global_token->s[0] == '{')
    {
        recursive_statement();
    }
    else if(':' == global_token->s[0])
    {
        emit_out(global_token->s);
        emit_out("\t#C goto label\n");
        global_token = global_token->next;
    }
    else if((NULL != lookup_type(global_token->s, prim_types)) ||
              match("struct", global_token->s))
    {
        collect_local();
    }
    else if(match("if", global_token->s))
    {
        process_if();
    }
    else if(match("switch", global_token->s))
    {
        process_switch();
    }
    else if(match("do", global_token->s))
    {
        process_do();
    }
    else if(match("while", global_token->s))
    {
        process_while();
    }
    else if(match("for", global_token->s))
    {
        process_for();
    }
    else if(match("asm", global_token->s))
    {
        process_asm();
    }
    else if(match("goto", global_token->s))
    {
        global_token = global_token->next;
        require(NULL != global_token, "naked goto is not supported\n");
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @");
        else if(X86 == Architecture) emit_out("jmp %");
        else if(AMD64 == Architecture) emit_out("jmp %");
        else if(ARMV7L == Architecture) emit_out("^~");
        else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$");
        emit_out(global_token->s);
        if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS");
        else if(AARCH64 == Architecture) emit_out("\nBR_X16");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" jal");
        emit_out("\n");
        global_token = global_token->next;
        require_match("ERROR in statement\nMissing ;\n", ";");
    }
    else if(match("return", global_token->s))
    {
        return_result();
    }
    else if(match("break", global_token->s))
    {
        process_break();
    }
    else if(match("continue", global_token->s))
    {
        process_continue();
    }
    else
    {
        expression();
        require_match("ERROR in statement\nMISSING ;\n", ";");
    }
}

/* Collect function arguments */
void collect_arguments()
{
    global_token = global_token->next;
    require(NULL != global_token, "Received EOF when attempting to collect arguments\n");
    struct type* type_size;
    struct token_list* a;

    while(!match(")", global_token->s))
    {
        type_size = type_name();
        require(NULL != global_token, "Received EOF when attempting to collect arguments\n");
        require(NULL != type_size, "Must have non-null type\n");
        if(global_token->s[0] == ')')
        {
            /* foo(int,char,void) doesn't need anything done */
            continue;
        }
        else if(global_token->s[0] != ',')
        {
            /* deal with foo(int a, char b) */
            require(!in_set(global_token->s[0], "[{(<=>)}]|&!^%;:'\""), "forbidden character in argument variable name\n");
            require(!iskeywordp(global_token->s), "You are not allowed to use a keyword as a argument variable name\n");
            a = sym_declare(global_token->s, type_size, function->arguments);
            if(NULL == function->arguments)
            {
                if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = 0;
                else if(X86 == Architecture) a->depth = -4;
                else if(AMD64 == Architecture) a->depth = -8;
                else if(ARMV7L == Architecture) a->depth = 4;
                else if(AARCH64 == Architecture) a->depth = register_size;
                else if(RISCV32 == Architecture) a->depth = -4;
                else if(RISCV64 == Architecture) a->depth = -8;
            }
            else
            {
                if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = function->arguments->depth + register_size;
                else if(X86 == Architecture) a->depth = function->arguments->depth - register_size;
                else if(AMD64 == Architecture) a->depth = function->arguments->depth - register_size;
                else if(ARMV7L == Architecture) a->depth = function->arguments->depth + register_size;
                else if(AARCH64 == Architecture) a->depth = function->arguments->depth + register_size;
                else if(RISCV32 == Architecture) a->depth = function->arguments->depth - register_size;
                else if(RISCV64 == Architecture) a->depth = function->arguments->depth - register_size;
            }

            global_token = global_token->next;
            require(NULL != global_token, "Incomplete argument list\n");
            function->arguments = a;
        }

        /* ignore trailing comma (needed for foo(bar(), 1); expressions*/
        if(global_token->s[0] == ',')
        {
            global_token = global_token->next;
            require(NULL != global_token, "naked comma in collect arguments\n");
        }

        require(NULL != global_token, "Argument list never completed\n");
    }
    global_token = global_token->next;
}

void declare_function()
{
    current_count = 0;
    function = sym_declare(global_token->prev->s, NULL, global_function_list);

    /* allow previously defined functions to be looked up */
    global_function_list = function;
    if((KNIGHT_NATIVE == Architecture) && match("main", function->s))
    {
        require_match("Impossible error ( vanished\n", "(");
        require_match("Reality ERROR (USING KNIGHT-NATIVE)\nHardware does not support arguments\nthus neither can main on this architecture\ntry tape_01 and tape_02 instead\n", ")");
    }
    else collect_arguments();

    require(NULL != global_token, "Function definitions either need to be prototypes or full\n");
    /* If just a prototype don't waste time */
    if(global_token->s[0] == ';') global_token = global_token->next;
    else
    {
        emit_out("# Defining function ");
        emit_out(function->s);
        emit_out("\n");
        emit_out(":FUNCTION_");
        emit_out(function->s);
        emit_out("\n");
        statement();

        /* Prevent duplicate RETURNS */
        if(((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) && !match("RET R15\n", output_list->s)) emit_out("RET R15\n");
        else if((X86 == Architecture) && !match("ret\n", output_list->s)) emit_out("ret\n");
        else if((AMD64 == Architecture) && !match("ret\n", output_list->s)) emit_out("ret\n");
        else if((ARMV7L == Architecture) && !match("'1' LR RETURN\n", output_list->s)) emit_out("'1' LR RETURN\n");
        else if((AARCH64 == Architecture) && !match("RETURN\n", output_list->s)) emit_out("RETURN\n");
        else if((RISCV32 == Architecture) && !match("ret\n", output_list->s)) emit_out("ret\n");
        else if((RISCV64 == Architecture) && !match("ret\n", output_list->s)) emit_out("ret\n");
    }
}

void global_constant()
{
    global_token = global_token->next;
    require(NULL != global_token, "CONSTANT lacks a name\n");
    global_constant_list = sym_declare(global_token->s, NULL, global_constant_list);

    require(NULL != global_token->next, "CONSTANT lacks a value\n");
    if(match("sizeof", global_token->next->s))
    {
        global_token = global_token->next->next;
        require_match("ERROR in CONSTANT with sizeof\nMissing (\n", "(");
        struct type* a = type_name();
        require_match("ERROR in CONSTANT with sizeof\nMissing )\n", ")");
        global_token->prev->s = int2str(a->size, 10, TRUE);
        global_constant_list->arguments = global_token->prev;
    }
    else
    {
        global_constant_list->arguments = global_token->next;
        global_token = global_token->next->next;
    }
}

struct type* global_typedef()
{
    struct type* type_size;
    /* typedef $TYPE $NAME; */
    global_token = global_token->next;
    type_size = type_name();
    require(NULL != global_token, "Received EOF while reading typedef\n");
    type_size = mirror_type(type_size, global_token->s);
    add_primitive(type_size);
    global_token = global_token->next;
    require_match("ERROR in typedef statement\nMissing ;\n", ";");
    return type_size;
}

void global_static_array(struct type* type_size, struct token_list* name)
{
    int size;
    maybe_bootstrap_error("global array definitions");
    globals_list = emit(":GLOBAL_", globals_list);
    globals_list = emit(name->s, globals_list);
    globals_list = emit("\n&GLOBAL_STORAGE_", globals_list);
    globals_list = emit(name->s, globals_list);
    if (AARCH64 == Architecture || AMD64 == Architecture || RISCV64 == Architecture)
    {
        globals_list = emit(" %0", globals_list);
    }
    globals_list = emit("\n:GLOBAL_STORAGE_", globals_list);
    globals_list = emit(name->s, globals_list);

    require(NULL != global_token->next, "Unterminated global\n");
    global_token = global_token->next;

    /* Make sure not negative */
    if(match("-", global_token->s))
    {
        line_error();
        fputs("Negative values are not supported for allocated arrays\n", stderr);
        exit(EXIT_FAILURE);
    }

    /* length */
    size = strtoint(global_token->s) * type_size->size;

    /* Stop bad states */
    if((size < 0) || (size > 0x100000))
    {
        line_error();
        fputs("M2-Planet is very inefficient so you probably don't want to allocate over 1MB into your binary for NULLs\n", stderr);
        exit(EXIT_FAILURE);
    }

    /* Ensure properly closed */
    global_token = global_token->next;
    require_match("missing close bracket\n", "]");
    require_match("missing ;\n", ";");

    globals_list = emit("\n'", globals_list);
    while (0 != size)
    {
        globals_list = emit(" 00", globals_list);
        size = size - 1;
    }
    globals_list = emit("'\n", globals_list);
}

void global_assignment()
{
    /* Store the global's value*/
    globals_list = emit(":GLOBAL_", globals_list);
    globals_list = emit(global_token->prev->s, globals_list);
    globals_list = emit("\n", globals_list);
    global_token = global_token->next;
    require(NULL != global_token, "Global locals value in assignment\n");
    unsigned padding_zeroes;
    if(in_set(global_token->s[0], "0123456789"))
    { /* Assume Int */
        globals_list = emit("%", globals_list);
        globals_list = emit(global_token->s, globals_list);

        /* broken for big endian architectures */
        padding_zeroes = (register_size / 4) - 1;
        while(padding_zeroes > 0)
        {
            /* Assume positive Int */
            globals_list = emit(" %0", globals_list);
            padding_zeroes = padding_zeroes - 1;
        }
        globals_list = emit("\n", globals_list);
    }
    else if(('"' == global_token->s[0]))
    { /* Assume a string*/
        globals_list = emit("&GLOBAL_", globals_list);
        globals_list = emit(global_token->prev->prev->s, globals_list);
        globals_list = emit("_contents\n", globals_list);

        globals_list = emit(":GLOBAL_", globals_list);
        globals_list = emit(global_token->prev->prev->s, globals_list);
        globals_list = emit("_contents\n", globals_list);
        globals_list = emit(parse_string(global_token->s), globals_list);
    }
    else
    {
        line_error();
        fputs("Received ", stderr);
        fputs(global_token->s, stderr);
        fputs(" in program\n", stderr);
        exit(EXIT_FAILURE);
    }

    global_token = global_token->next;
    require_match("ERROR in Program\nMissing ;\n", ";");
}

/*
 * program:
 *     declaration
 *     declaration program
 *
 * declaration:
 *     CONSTANT identifer value
 *     typedef identifer type;
 *     type-name identifier ;
 *     type-name identifier = value ;
 *     type-name identifier [ value ];
 *     type-name identifier ( parameter-list ) ;
 *     type-name identifier ( parameter-list ) statement
 *
 * parameter-list:
 *     parameter-declaration
 *     parameter-list, parameter-declaration
 *
 * parameter-declaration:
 *     type-name identifier-opt
 */
void program()
{
    unsigned i;
    function = NULL;
    Address_of = FALSE;
    struct type* type_size;

new_type:
    /* Deal with garbage input */
    if (NULL == global_token) return;
    require('#' != global_token->s[0], "unhandled macro directive\n");
    require(!match("\n", global_token->s), "unexpected newline token\n");

    /* Handle cc_* CONSTANT statements */
    if(match("CONSTANT", global_token->s))
    {
        global_constant();
        goto new_type;
    }

    /* Handle c typedef statements */
    if(match("typedef", global_token->s))
    {
        type_size = global_typedef();
        goto new_type;
    }

    type_size = type_name();
    /* Deal with case of struct definitions */
    if(NULL == type_size) goto new_type;

    require(NULL != global_token->next, "Unterminated global\n");

    /* Add to global symbol table */
    global_symbol_list = sym_declare(global_token->s, type_size, global_symbol_list);
    global_token = global_token->next;

    /* Deal with global variables */
    if(match(";", global_token->s))
    {
        /* Ensure enough bytes are allocated to store global variable.
           In some cases it allocates too much but that is harmless. */
        globals_list = emit(":GLOBAL_", globals_list);
        globals_list = emit(global_token->prev->s, globals_list);

        /* round up division */
        i = ceil_div(type_size->size, register_size);
        globals_list = emit("\n", globals_list);
        while(i != 0)
        {
            globals_list = emit("NULL\n", globals_list);
            i = i - 1;
        }
        global_token = global_token->next;
        goto new_type;
    }

    /* Deal with global functions */
    if(match("(", global_token->s))
    {
        declare_function();
        goto new_type;
    }

    /* Deal with assignment to a global variable */
    if(match("=", global_token->s))
    {
        global_assignment();
        goto new_type;
    }

    /* Deal with global static arrays */
    if(match("[", global_token->s))
    {
        global_static_array(type_size, global_token->prev);
        goto new_type;
    }

    /* Everything else is just an error */
    line_error();
    fputs("Received ", stderr);
    fputs(global_token->s, stderr);
    fputs(" in program\n", stderr);
    exit(EXIT_FAILURE);
}

void recursive_output(struct token_list* head, FILE* out)
{
    struct token_list* i = reverse_list(head);
    while(NULL != i)
    {
        fputs(i->s, out);
        i = i->next;
    }
}

void output_tokens(struct token_list *i, FILE* out)
{
    while(NULL != i)
    {
        fputs(i->s, out);
        fputs(" ", out);
        i = i->next;
    }
}

File /M2-Planet/cc_macro.c

Live-bootstrap source file is 'seed/stage0-posix/M2-Planet/cc_macro.c'.
URL: https://github.com/oriansj/M2-Planet/blob/0479606ed5131a64cd1dd2dd3f257e73cd9dce68/cc_macro.c
/* Copyright (C) 2021 Sanne Wouda
 * Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
 * Copyright (C) 2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "cc.h"
#include "gcc_req.h"

void require(int bool, char* error);
int strtoint(char* a);
void line_error_token(struct token_list* list);
struct token_list* eat_token(struct token_list* head);

struct conditional_inclusion
{
    struct conditional_inclusion* prev;
    int include; /* 1 == include, 0 == skip */
    int previous_condition_matched; /* 1 == all subsequent conditions treated as FALSE */
};

struct macro_list
{
    struct macro_list* next;
    char* symbol;
    struct token_list* expansion;
};

struct macro_list* macro_env;
struct conditional_inclusion* conditional_inclusion_top;

/* point where we are currently modifying the global_token list */
struct token_list* macro_token;

void init_macro_env(char* sym, char* value, char* source, int num)
{
    struct macro_list* hold = macro_env;
    macro_env = calloc(1, sizeof(struct macro_list));
    macro_env->symbol = sym;
    macro_env->next = hold;
    macro_env->expansion = calloc(1, sizeof(struct token_list));
    macro_env->expansion->s = value;
    macro_env->expansion->filename = source;
    macro_env->expansion->linenumber = num;
}

void eat_current_token()
{
    int update_global_token = FALSE;
    if (macro_token == global_token)
        update_global_token = TRUE;

    macro_token = eat_token(macro_token);

    if(update_global_token)
        global_token = macro_token;
}

void eat_newline_tokens()
{
    macro_token = global_token;

    while(TRUE)
    {
        if(NULL == macro_token) return;

        if(match("\n", macro_token->s))
        {
            eat_current_token();
        }
        else
        {
            macro_token = macro_token->next;
        }
    }
}

/* returns the first token inserted; inserts *before* point */
struct token_list* insert_tokens(struct token_list* point, struct token_list* token)
{
    struct token_list* copy;
    struct token_list* first = NULL;

    while (NULL != token)
    {
        copy = calloc(1, sizeof(struct token_list));
        copy->s = token->s;
        copy->filename = token->filename;
        copy->linenumber = token->linenumber;

        if(NULL == first)
        {
            first = copy;
        }

        copy->next = point;

        if (NULL != point)
        {
            copy->prev = point->prev;

            if(NULL != point->prev)
            {
                point->prev->next = copy;
            }

            point->prev = copy;
        }

        token = token->next;
    }

    return first;
}

struct macro_list* lookup_macro(struct token_list* token)
{
    if(NULL == token)
    {
        line_error_token(macro_token);
        fputs("null token received in lookup_macro\n", stderr);
        exit(EXIT_FAILURE);
    }

    struct macro_list* hold = macro_env;

    while (NULL != hold)
    {
        if (match(token->s, hold->symbol))
        {
            /* found! */
            return hold;
        }

        hold = hold->next;
    }

    /* not found! */
    return NULL;
}

void remove_macro(struct token_list* token)
{
    if(NULL == token)
    {
        line_error_token(macro_token);
        fputs("received a null in remove_macro\n", stderr);
        exit(EXIT_FAILURE);
    }

    struct macro_list* hold = macro_env;
    struct macro_list* temp;

    /* Deal with the first element */
    if (match(token->s, hold->symbol)) {
        macro_env = hold->next;
        free(hold);
        return;
    }

    /* Remove element form the middle of linked list */
    while (NULL != hold->next)
    {
        if (match(token->s, hold->next->symbol))
        {
            temp = hold->next;
            hold->next = hold->next->next;
            free(temp);
            return;
        }

        hold = hold->next;
    }

    /* nothing to undefine */
    return;
}

int macro_expression();
int macro_variable()
{
    int value = 0;
    struct macro_list* hold = lookup_macro(macro_token);
    if (NULL != hold)
    {
        if(NULL == hold->expansion)
        {
            line_error_token(macro_token);
            fputs("hold->expansion is a null\n", stderr);
            exit(EXIT_FAILURE);
        }
        value = strtoint(hold->expansion->s);
    }
    eat_current_token();
    return value;
}

int macro_number()
{
    int result = strtoint(macro_token->s);
    eat_current_token();
    return result;
}

int macro_primary_expr()
{
    int defined_has_paren = FALSE;
    int hold;
    require(NULL != macro_token, "got an EOF terminated macro primary expression\n");

    if('-' == macro_token->s[0])
    {
        eat_current_token();
        return -macro_primary_expr();
    }
    else if('!' == macro_token->s[0])
    {
        eat_current_token();
        return !macro_primary_expr();
    }
    else if('(' == macro_token->s[0])
    {
        eat_current_token();
        hold = macro_expression();
        require(')' == macro_token->s[0], "missing ) in macro expression\n");
        eat_current_token();
        return hold;
    }
    else if(match("defined", macro_token->s))
    {
        eat_current_token();

        require(NULL != macro_token, "got an EOF terminated macro defined expression\n");

        if('(' == macro_token->s[0])
        {
            defined_has_paren = TRUE;
            eat_current_token();
        }

        if (NULL != lookup_macro(macro_token))
        {
            hold = TRUE;
        }
        else
        {
            hold = FALSE;
        }
        eat_current_token();

        if(TRUE == defined_has_paren)
        {
            if(NULL == macro_token)
            {
                line_error_token(macro_token);
                fputs("unterminated define ( statement\n", stderr);
                exit(EXIT_FAILURE);
            }
            require(')' == macro_token->s[0], "missing close parenthesis for defined()\n");
            eat_current_token();
        }

        return hold;
    }
    else if(in_set(macro_token->s[0], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"))
    {
        return macro_variable();
    }
    else if(in_set(macro_token->s[0], "0123456789"))
    {
        return macro_number();
    }
    else
    {
        return 0;    /* FIXME: error handling */
    }
}

int macro_additive_expr()
{
    int lhs = macro_primary_expr();
    int hold;

    require(NULL != macro_token, "got an EOF terminated macro additive expression\n");
    if(match("+", macro_token->s))
    {
        eat_current_token();
        return lhs + macro_additive_expr();
    }
    else if(match("-", macro_token->s))
    {
        eat_current_token();
        return lhs - macro_additive_expr();
    }
    else if(match("*", macro_token->s))
    {
        eat_current_token();
        return lhs * macro_additive_expr();
    }
    else if(match("/", macro_token->s))
    {
        eat_current_token();
        hold = macro_additive_expr();
        require(0 != hold, "divide by zero not valid even in C macros\n");
        return lhs / hold;
    }
    else if(match("%", macro_token->s))
    {
        eat_current_token();
        hold = macro_additive_expr();
        require(0 != hold, "modulus by zero not valid even in C macros\n");
        return lhs % hold;
    }
    else if(match(">>", macro_token->s))
    {
        eat_current_token();
        return lhs >> macro_additive_expr();
    }
    else if(match("<<", macro_token->s))
    {
        eat_current_token();
        return lhs << macro_additive_expr();
    }
    else
    {
        return lhs;
    }
}

int macro_relational_expr()
{
    int lhs = macro_additive_expr();

    if(match("<", macro_token->s))
    {
        eat_current_token();
        return lhs < macro_relational_expr();
    }
    else if(match("<=", macro_token->s))
    {
        eat_current_token();
        return lhs <= macro_relational_expr();
    }
    else if(match(">=", macro_token->s))
    {
        eat_current_token();
        return lhs >= macro_relational_expr();
    }
    else if(match(">", macro_token->s))
    {
        eat_current_token();
        return lhs > macro_relational_expr();
    }
    else if(match("==", macro_token->s))
    {
        eat_current_token();
        return lhs == macro_relational_expr();
    }
    else if(match("!=", macro_token->s))
    {
        eat_current_token();
        return lhs != macro_relational_expr();
    }
    else
    {
        return lhs;
    }
}

int macro_bitwise_expr()
{
    int rhs;
    int lhs = macro_relational_expr();

    if(match("&", macro_token->s))
    {
        eat_current_token();
        return lhs & macro_bitwise_expr();
    }
    else if(match("&&", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs && rhs;
    }
    else if(match("|", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs | rhs;
    }
    else if(match("||", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs || rhs;
    }
    else if(match("^", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs ^ rhs;
    }
    else
    {
        return lhs;
    }
}

int macro_expression()
{
    return macro_bitwise_expr();
}

void handle_define()
{
    struct macro_list* hold;
    struct token_list* expansion_end = NULL;

    /* don't use #define statements from non-included blocks */
    int conditional_define = TRUE;
    if(NULL != conditional_inclusion_top)
    {
        if(FALSE == conditional_inclusion_top->include)
        {
            conditional_define = FALSE;
        }
    }

    eat_current_token();

    require(NULL != macro_token, "got an EOF terminated #define\n");
    require('\n' != macro_token->s[0], "unexpected newline after #define\n");

    /* insert new macro */
    hold = calloc(1, sizeof(struct macro_list));
    hold->symbol = macro_token->s;
    hold->next = macro_env;
    /* provided it isn't in a non-included block */
    if(conditional_define) macro_env = hold;

    /* discard the macro name */
    eat_current_token();

    while (TRUE)
    {
        require(NULL != macro_token, "got an EOF terminated #define\n");

        if ('\n' == macro_token->s[0])
        {
            if(NULL == expansion_end)
            {
                hold->expansion = NULL;
                expansion_end = macro_token;
                return;
            }
            expansion_end->next = NULL;
            return;
        }

        require(NULL != hold, "#define got something it can't handle\n");

        expansion_end = macro_token;

        /* in the first iteration, we set the first token of the expansion, if
           it exists */
        if (NULL == hold->expansion)
        {
            hold->expansion = macro_token;
        }

        /* throw away if not used */
        if(!conditional_define && (NULL != hold))
        {
            free(hold);
            hold = NULL;
        }

        eat_current_token();
    }
}

void handle_undef()
{
    eat_current_token();
    remove_macro(macro_token);
    eat_current_token();
}

void handle_error(int warning_p)
{
    /* don't use #error statements from non-included blocks */
    int conditional_error = TRUE;
    if(NULL != conditional_inclusion_top)
    {
        if(FALSE == conditional_inclusion_top->include)
        {
            conditional_error = FALSE;
        }
    }
    eat_current_token();
    /* provided it isn't in a non-included block */
    if(conditional_error)
    {
        line_error_token(macro_token);
        if(warning_p) fputs(" warning: #warning ", stderr);
        else fputs(" error: #error ", stderr);
        while (TRUE)
        {
            require(NULL != macro_token, "\nFailed to properly terminate error message with \\n\n");
            if ('\n' == macro_token->s[0]) break;
            fputs(macro_token->s, stderr);
            macro_token = macro_token->next;
            fputs(" ", stderr);
        }
        fputs("\n", stderr);
        if(!warning_p) exit(EXIT_FAILURE);
    }
    while (TRUE)
    {
        require(NULL != macro_token, "\nFailed to properly terminate error message with \\n\n");
        /* discard the error */
        if ('\n' == macro_token->s[0])
        {
            return;
        }
        eat_current_token();
    }
}

void eat_block();
void macro_directive()
{
    struct conditional_inclusion *t;
    int result;

    /* FIXME: whitespace is allowed between "#"" and "if" */
    if(match("#if", macro_token->s))
    {
        eat_current_token();
        /* evaluate constant integer expression */
        result = macro_expression();
        /* push conditional inclusion */
        t = calloc(1, sizeof(struct conditional_inclusion));
        t->prev = conditional_inclusion_top;
        conditional_inclusion_top = t;
        t->include = TRUE;

        if(FALSE == result)
        {
            t->include = FALSE;
            eat_block();
        }

        t->previous_condition_matched = t->include;
    }
    else if(match("#ifdef", macro_token->s))
    {
        eat_current_token();
        require(NULL != macro_token, "got an EOF terminated macro defined expression\n");
        if (NULL != lookup_macro(macro_token))
        {
            result = TRUE;
            eat_current_token();
        }
        else
        {
            result = FALSE;
            eat_block();
        }

        /* push conditional inclusion */
        t = calloc(1, sizeof(struct conditional_inclusion));
        t->prev = conditional_inclusion_top;
        conditional_inclusion_top = t;
        t->include = TRUE;

        if(FALSE == result)
        {
            t->include = FALSE;
        }

        t->previous_condition_matched = t->include;
    }
    else if(match("#ifndef", macro_token->s))
    {
        eat_current_token();
        require(NULL != macro_token, "got an EOF terminated macro defined expression\n");
        if (NULL != lookup_macro(macro_token))
        {
            result = FALSE;
        }
        else
        {
            result = TRUE;
            eat_current_token();
        }

        /* push conditional inclusion */
        t = calloc(1, sizeof(struct conditional_inclusion));
        t->prev = conditional_inclusion_top;
        conditional_inclusion_top = t;
        t->include = TRUE;

        if(FALSE == result)
        {
            t->include = FALSE;
            eat_block();
        }

        t->previous_condition_matched = t->include;
    }
    else if(match("#elif", macro_token->s))
    {
        eat_current_token();
        result = macro_expression();
        require(NULL != conditional_inclusion_top, "#elif without leading #if\n");
        conditional_inclusion_top->include = result && !conditional_inclusion_top->previous_condition_matched;
        conditional_inclusion_top->previous_condition_matched =
            conditional_inclusion_top->previous_condition_matched || conditional_inclusion_top->include;
        if(FALSE == result)
        {
            eat_block();
        }
    }
    else if(match("#else", macro_token->s))
    {
        eat_current_token();
        require(NULL != conditional_inclusion_top, "#else without leading #if\n");
        conditional_inclusion_top->include = !conditional_inclusion_top->previous_condition_matched;
        if(FALSE == conditional_inclusion_top->include)
        {
            eat_block();
        }
    }
    else if(match("#endif", macro_token->s))
    {
        if(NULL == conditional_inclusion_top)
        {
            line_error_token(macro_token);
            fputs("unexpected #endif\n", stderr);
            exit(EXIT_FAILURE);
        }

        eat_current_token();
        /* pop conditional inclusion */
        t = conditional_inclusion_top;
        conditional_inclusion_top = conditional_inclusion_top->prev;
        free(t);
    }
    else if(match("#define", macro_token->s))
    {
        handle_define();
    }
    else if(match("#undef", macro_token->s))
    {
        handle_undef();
    }
    else if(match("#error", macro_token->s))
    {
        handle_error(FALSE);
    }
    else if(match("#warning", macro_token->s))
    {
        handle_error(TRUE);
    }
    else
    {
        if(!match("#include", macro_token->s))
        {
            /* Put a big fat warning but see if we can just ignore */
            fputs(">>WARNING<<\n>>WARNING<<\n", stderr);
            line_error_token(macro_token);
            fputs("feature: ", stderr);
            fputs(macro_token->s, stderr);
            fputs(" unsupported in M2-Planet\nIgnoring line, may result in bugs\n>>WARNING<<\n>>WARNING<<\n\n", stderr);
        }

        /* unhandled macro directive; let's eat until a newline; om nom nom */
        while(TRUE)
        {
            if(NULL == macro_token)
            {
                return;
            }

            if('\n' == macro_token->s[0])
            {
                return;
            }

            eat_current_token();
        }
    }
}


void eat_until_endif()
{
    /* This #if block is nested inside of an #if block that needs to be dropped, lose EVERYTHING */
    do
    {
        require(NULL != macro_token, "Unterminated #if block\n");
        if(match("#if", macro_token->s) || match("#ifdef", macro_token->s) || match("#ifndef", macro_token->s))
        {
            eat_current_token();
            eat_until_endif();
        }

        eat_current_token();
        require(NULL != macro_token, "Unterminated #if block\n");
    } while(!match("#endif", macro_token->s));
}

void eat_block()
{
    /* This conditional #if block is wrong, drop everything until the #elif/#else/#endif */
    do
    {
        if(match("#if", macro_token->s) || match("#ifdef", macro_token->s) || match("#ifndef", macro_token->s))
        {
            eat_current_token();
            eat_until_endif();
        }

        eat_current_token();
        require(NULL != macro_token, "Unterminated #if block\n");
        if(match("#elif", macro_token->s)) break;
        if(match("#else", macro_token->s)) break;
        if(match("#endif", macro_token->s)) break;
    } while(TRUE);
    require(NULL != macro_token->prev, "impossible #if block\n");

    /* rewind the newline */
    if(match("\n", macro_token->prev->s)) macro_token = macro_token->prev;
}


struct token_list* maybe_expand(struct token_list* token)
{
    if(NULL == token)
    {
        line_error_token(macro_token);
        fputs("maybe_expand passed a null token\n", stderr);
        exit(EXIT_FAILURE);
    }

    struct macro_list* hold = lookup_macro(token);
    struct token_list* hold2;
    if(NULL == token->next)
    {
        line_error_token(macro_token);
        fputs("we can't expand a null token: ", stderr);
        fputs(token->s, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }

    if (NULL == hold)
    {
        return token->next;
    }

    token = eat_token(token);

    if (NULL == hold->expansion)
    {
        return token->next;
    }
    hold2 = insert_tokens(token, hold->expansion);

    return hold2->next;
}

void preprocess()
{
    int start_of_line = TRUE;
    macro_token = global_token;

    while(NULL != macro_token)
    {
        if(start_of_line && '#' == macro_token->s[0])
        {
            macro_directive();

            if(macro_token)
            {
                if('\n' != macro_token->s[0])
                {
                    line_error_token(macro_token);
                    fputs("newline expected at end of macro directive\n", stderr);
                    fputs("found: '", stderr);
                    fputs(macro_token->s, stderr);
                    fputs("'\n", stderr);
                    exit(EXIT_FAILURE);
                }
            }
        }
        else if('\n' == macro_token->s[0])
        {
            start_of_line = TRUE;
            macro_token = macro_token->next;
        }
        else
        {
            start_of_line = FALSE;
            if(NULL == conditional_inclusion_top)
            {
                macro_token = maybe_expand(macro_token);
            }
            else if(!conditional_inclusion_top->include)
            {
                /* rewrite the token stream to exclude the current token */
                eat_block();
                start_of_line = TRUE;
            }
            else
            {
                macro_token = maybe_expand(macro_token);
            }
        }
    }
}

File /M2-Planet/cc.c

Live-bootstrap source file is 'seed/stage0-posix/M2-Planet/cc.c'.
URL: https://github.com/oriansj/M2-Planet/blob/0479606ed5131a64cd1dd2dd3f257e73cd9dce68/cc.c
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include"cc.h"

/* The core functions */
void initialize_types();
struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename);
struct token_list* reverse_list(struct token_list* head);

struct token_list* remove_line_comments(struct token_list* head);
struct token_list* remove_line_comment_tokens(struct token_list* head);
struct token_list* remove_preprocessor_directives(struct token_list* head);

void eat_newline_tokens();
void init_macro_env(char* sym, char* value, char* source, int num);
void preprocess();
void program();
void recursive_output(struct token_list* i, FILE* out);
void output_tokens(struct token_list *i, FILE* out);
int strtoint(char *a);

int main(int argc, char** argv)
{
    MAX_STRING = 4096;
    BOOTSTRAP_MODE = FALSE;
    PREPROCESSOR_MODE = FALSE;
    int DEBUG = FALSE;
    FILE* in = stdin;
    FILE* destination_file = stdout;
    Architecture = 0; /* catch unset */
    init_macro_env("__M2__", "42", "__INTERNAL_M2__", 0); /* Setup __M2__ */
    char* arch;
    char* name;
    char* hold;
    int env=0;
    char* val;

    int i = 1;
    while(i <= argc)
    {
        if(NULL == argv[i])
        {
            i = i + 1;
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            if(NULL == hold_string)
            {
                hold_string = calloc(MAX_STRING + 4, sizeof(char));
                require(NULL != hold_string, "Impossible Exhaustion has occurred\n");
            }

            name = argv[i + 1];
            if(NULL == name)
            {
                fputs("did not receive a file name\n", stderr);
                exit(EXIT_FAILURE);
            }

            in = fopen(name, "r");
            if(NULL == in)
            {
                fputs("Unable to open for reading file: ", stderr);
                fputs(name, stderr);
                fputs("\n Aborting to avoid problems\n", stderr);
                exit(EXIT_FAILURE);
            }
            global_token = read_all_tokens(in, global_token, name);
            fclose(in);
            i = i + 2;
        }
        else if(match(argv[i], "-o") || match(argv[i], "--output"))
        {
            destination_file = fopen(argv[i + 1], "w");
            if(NULL == destination_file)
            {
                fputs("Unable to open for writing file: ", stderr);
                fputs(argv[i + 1], stderr);
                fputs("\n Aborting to avoid problems\n", stderr);
                exit(EXIT_FAILURE);
            }
            i = i + 2;
        }
        else if(match(argv[i], "-A") || match(argv[i], "--architecture"))
        {
            arch = argv[i + 1];
            if(match("knight-native", arch)) {
                Architecture = KNIGHT_NATIVE;
                init_macro_env("__knight__", "1", "--architecture", env);
                env = env + 1;
            }
            else if(match("knight-posix", arch)) {
                Architecture = KNIGHT_POSIX;
                init_macro_env("__knight_posix__", "1", "--architecture", env);
                env = env + 1;
            }
            else if(match("x86", arch))
            {
                Architecture = X86;
                init_macro_env("__i386__", "1", "--architecture", env);
                env = env + 1;
            }
            else if(match("amd64", arch))
            {
                Architecture = AMD64;
                init_macro_env("__x86_64__", "1", "--architecture", env);
                env = env + 1;
            }
            else if(match("armv7l", arch))
            {
                Architecture = ARMV7L;
                init_macro_env("__arm__", "1", "--architecture", env);
                env = env + 1;
            }
            else if(match("aarch64", arch))
            {
                Architecture = AARCH64;
                init_macro_env("__aarch64__", "1", "--architecture", env);
                env = env + 1;
            }
            else if(match("riscv32", arch))
            {
                Architecture = RISCV32;
                init_macro_env("__riscv", "1", "--architecture", env);
                init_macro_env("__riscv_xlen", "32", "--architecture", env + 1);
                env = env + 2;
            }
            else if(match("riscv64", arch))
            {
                Architecture = RISCV64;
                init_macro_env("__riscv", "1", "--architecture", env);
                init_macro_env("__riscv_xlen", "64", "--architecture", env + 1);
                env = env + 2;
            }
            else
            {
                fputs("Unknown architecture: ", stderr);
                fputs(arch, stderr);
                fputs(" know values are: knight-native, knight-posix, x86, amd64, armv7l, aarch64, riscv32 and riscv64\n", stderr);
                exit(EXIT_FAILURE);
            }
            i = i + 2;
        }
        else if(match(argv[i], "--max-string"))
        {
            hold = argv[i+1];
            if(NULL == hold)
            {
                fputs("--max-string requires a numeric argument\n", stderr);
                exit(EXIT_FAILURE);
            }
            MAX_STRING = strtoint(hold);
            require(0 < MAX_STRING, "Not a valid string size\nAbort and fix your --max-string\n");
            i = i + 2;
        }
        else if(match(argv[i], "--bootstrap-mode"))
        {
            BOOTSTRAP_MODE = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "-g") || match(argv[i], "--debug"))
        {
            DEBUG = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs(" -f input file\n -o output file\n --help for this message\n --version for file version\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-E"))
        {
            PREPROCESSOR_MODE = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "-D"))
        {
            val = argv[i+1];
            if(NULL == val)
            {
                fputs("-D requires an argument", stderr);
                exit(EXIT_FAILURE);
            }
            while(0 != val[0])
            {
                if('=' == val[0])
                {
                    val[0] = 0;
                    val = val + 1;
                    break;
                }
                val = val + 1;
            }
            init_macro_env(argv[i+1], val, "__ARGV__", env);
            env = env + 1;
            i = i + 2;
        }
        else if(match(argv[i], "-V") || match(argv[i], "--version"))
        {
            fputs("M2-Planet v1.11.0\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("UNKNOWN ARGUMENT\n", stdout);
            exit(EXIT_FAILURE);
        }
    }

    /* Deal with special case of architecture not being set */
    if(0 == Architecture)
    {
        Architecture = KNIGHT_NATIVE;
        init_macro_env("__knight__", "1", "--architecture", env);
    }

    /* Deal with special case of wanting to read from standard input */
    if(stdin == in)
    {
        hold_string = calloc(MAX_STRING + 4, sizeof(char));
        require(NULL != hold_string, "Impossible Exhaustion has occurred\n");
        global_token = read_all_tokens(in, global_token, "STDIN");
    }

    if(NULL == global_token)
    {
        fputs("Either no input files were given or they were empty\n", stderr);
        exit(EXIT_FAILURE);
    }
    global_token = reverse_list(global_token);

    if (BOOTSTRAP_MODE)
    {
        global_token = remove_line_comment_tokens(global_token);
        global_token = remove_preprocessor_directives(global_token);
    }
    else
    {
        global_token = remove_line_comments(global_token);
        preprocess();
    }

    if (PREPROCESSOR_MODE)
    {
        fputs("\n/* Preprocessed source */\n", destination_file);
        output_tokens(global_token, destination_file);
        goto exit_success;
    }

    /* the main parser doesn't know how to handle newline tokens */
    eat_newline_tokens();

    initialize_types();
    reset_hold_string();
    output_list = NULL;
    program();

    /* Output the program we have compiled */
    fputs("\n# Core program\n", destination_file);
    recursive_output(output_list, destination_file);
    if(KNIGHT_NATIVE == Architecture) fputs("\n", destination_file);
    else if(DEBUG) fputs("\n:ELF_data\n", destination_file);
    fputs("\n# Program global variables\n", destination_file);
    recursive_output(globals_list, destination_file);
    fputs("\n# Program strings\n", destination_file);
    recursive_output(strings_list, destination_file);
    if(KNIGHT_NATIVE == Architecture) fputs("\n:STACK\n", destination_file);
    else if(!DEBUG) fputs("\n:ELF_end\n", destination_file);

exit_success:
    if (destination_file != stdout)
    {
        fclose(destination_file);
    }
    return EXIT_SUCCESS;
}

File /x86/x86_defs.M1

Live-bootstrap source file is 'seed/stage0-posix/x86/x86_defs.M1'.
URL: https://github.com/oriansj/stage0-posix-x86/blob/83508012c952e3aa3f70a89fd7d85d9a1af6f305/x86_defs.M1
## Copyright (C) 2017 Jeremiah Orians
## This file is part of M2-Planet.
##
## M2-Planet is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## M2-Planet is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.


DEFINE add_eax, 81C0
DEFINE add_ebp, 81C5
DEFINE add_ebx,eax 01C3
DEFINE add_eax,ebp 01E8
DEFINE add_eax,ebx 01D8
DEFINE and_eax,ebx 21D8
DEFINE call E8
DEFINE call_eax FFD0
DEFINE cmp 39C3
DEFINE cdq 99
DEFINE div_ebx F7F3
DEFINE idiv_ebx F7FB
DEFINE int CD
DEFINE je 0F84
DEFINE jne 0F85
DEFINE jmp E9
DEFINE lea_eax,[ebp+DWORD] 8D85
DEFINE lea_eax,[esp+DWORD] 8D8424
DEFINE lea_ebx,[esp+DWORD] 8D9C24
DEFINE lea_ecx,[esp+DWORD] 8D8C24
DEFINE lea_edx,[esp+DWORD] 8D9424
DEFINE mov_eax,[esp+DWORD] 8B8424
DEFINE mov_eax,ebp 89E8
DEFINE mov_eax,ebx 89D8
DEFINE mov_eax,ebx 89D8
DEFINE mov_eax,edx 89D0
DEFINE mov_ebx,eax 89C3
DEFINE mov_ecx,eax 89C1
DEFINE mov_ecx,esp 89E1
DEFINE mov_edi,esp 89E7
DEFINE mov_ebp,edi 89fd
DEFINE mov_ebp,esp 89E5
DEFINE mov_eax, B8
DEFINE mov_ebx, BB
DEFINE mov_edx, BA
DEFINE mov_eax,[eax] 8B00
DEFINE mov_ebx,[ebx] 8B1B
DEFINE mov_ecx,[ecx] 8B09
DEFINE mov_edx,[edx] 8B12
DEFINE mov_[ebx],al 8803
DEFINE mov_[ebx],eax 8903
DEFINE movsx_eax,BYTE_PTR_[eax] 0FBE00
DEFINE movsx_ebx,BYTE_PTR_[ebx] 0FBE1B
DEFINE movzx_eax,al 0FB6C0
DEFINE mul_ebx F7E3
DEFINE imul_ebx F7EB
DEFINE NULL 00000000
DEFINE not_eax F7D0
DEFINE or_eax,ebx 09D8
DEFINE pop_eax 58
DEFINE pop_ebx 5B
DEFINE pop_ebp 5D
DEFINE pop_edi 5F
DEFINE push_eax 50
DEFINE push_ebx 53
DEFINE push_ebp 55
DEFINE push_edi 57
DEFINE ret C3
DEFINE sal_eax, C1E0
DEFINE sal_eax,cl D3E0
DEFINE shl_eax,cl D3E0
DEFINE sar_eax,cl D3F8
DEFINE shr_eax,cl D3E8
DEFINE seta_al 0F97C0
DEFINE setae_al 0F93C0
DEFINE setb_al 0F92C0
DEFINE setbe_al 0F96C0
DEFINE sete_al 0F94C0
DEFINE setle_al 0F9EC0
DEFINE setl_al 0F9CC0
DEFINE setge_al 0F9DC0
DEFINE setg_al 0F9FC0
DEFINE setne_al 0F95C0
DEFINE sub_ebx,eax 29C3
DEFINE test_eax,eax 85C0
DEFINE xchg_ebx,eax 93
DEFINE xor_eax,ebx 31D8

File /x86/libc-core.M1

Live-bootstrap source file is 'seed/stage0-posix/x86/libc-core.M1'.
URL: https://github.com/oriansj/stage0-posix-x86/blob/83508012c952e3aa3f70a89fd7d85d9a1af6f305/libc-core.M1
## Copyright (C) 2016 Jeremiah Orians
## This file is part of stage0.
##
## stage0 is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## stage0 is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with stage0.  If not, see <http://www.gnu.org/licenses/>.

:_start

    mov_ebp,esp                 ; Protect esp

    ;; Prepare argv
    lea_eax,[ebp+DWORD] %4      ; ARGV_address = EBP + 4
    push_eax                    ; Put argv on the stack

    ;; Prepare envp
    mov_eax,ebp                 ; Address we need to load from
    mov_eax,[eax]               ; Get ARGC
    add_eax, %2                 ; OFFSET = ARGC + 2
    sal_eax, !2                 ; OFFSET = OFFSET * WORDSIZE
    add_eax,ebp                 ; ENVP_address = ESP + OFFSET
    push_eax                    ; Put envp on the stack

    ;; Stack offset
    add_ebp, %4                 ; Fix ebp

    ;; Perform the main loop
    call %FUNCTION_main

    ;; Exit to kernel
    mov_ebx,eax                 ; Using the return code given by main
    mov_eax, %1                 ; Syscall exit
    int !0x80                   ; Exit with that code

File /mescc-tools/stringify.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools/stringify.c'.
URL: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=stringify.c;id=08d5a0679fa4dc00babe02306f1bc50703083389
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
 * This file is part of mescc-tools.
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

// CONSTANT HEX 16
#define HEX 16
// CONSTANT OCTAL 8
#define OCTAL 8
// CONSTANT BINARY 2
#define BINARY 2

/***********************************************************
 * Needed for current implementation of little endian      *
 * Can be used to support little bit endian instruction    *
 * sets if we ever find one that might be useful           *
 * But I seriously doubt it                                *
 ***********************************************************/
void reverseBitOrder(char* c, int ByteMode)
{
    if(NULL == c) return;
    if(0 == c[1]) return;
    int hold = c[0];

    if(HEX == ByteMode)
    {
        c[0] = c[1];
        c[1] = hold;
        reverseBitOrder(c+2, ByteMode);
    }
    else if(OCTAL == ByteMode)
    {
        c[0] = c[2];
        c[2] = hold;
        reverseBitOrder(c+3, ByteMode);
    }
    else if(BINARY == ByteMode)
    {
        c[0] = c[7];
        c[7] = hold;
        hold = c[1];
        c[1] = c[6];
        c[6] = hold;
        hold = c[2];
        c[2] = c[5];
        c[5] = hold;
        hold = c[3];
        c[3] = c[4];
        c[4] = hold;
        reverseBitOrder(c+8, ByteMode);
    }
}

void LittleEndian(char* start, int ByteMode)
{
    char* end = start;
    char* c = start;
    while(0 != end[0]) end = end + 1;
    int hold;
    for(end = end - 1; start < end; start = start + 1)
    {
        hold = start[0];
        start[0] = end[0];
        end[0] = hold;
        end = end - 1;
    }

    /* The above makes a reversed bit order */
    reverseBitOrder(c, ByteMode);
}

int hex2char(int c)
{
    if((c >= 0) && (c <= 9)) return (c + 48);
    else if((c >= 10) && (c <= 15)) return (c + 55);
    else return -1;
}

int stringify(char* s, int digits, int divisor, int value, int shift)
{
    int i = value;
    if(digits > 1)
    {
        i = stringify(s+1, (digits - 1), divisor, value, shift);
    }
    s[0] = hex2char(i & (divisor - 1));
    return (i >> shift);
}

File /mescc-tools/blood-elf.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools/blood-elf.c'.
URL: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=blood-elf.c;id=08d5a0679fa4dc00babe02306f1bc50703083389
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
 * Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "M2libc/bootstrappable.h"

// CONSTANT max_string 4096
#define max_string 4096
int BITSIZE;
int BigEndian;
// CONSTANT HEX 16
#define HEX 16
// CONSTANT OCTAL 8
#define OCTAL 8
// CONSTANT BINARY 2
#define BINARY 2


/* Strings needed for constants */
char* zero_8;
char* zero_16;
char* zero_32;
char* one_16;
char* one_32;
char* two_8;
char* two_32;
char* three_32;
char* six_32;
char* sixteen_32;
char* twentyfour_32;

/* Imported from stringify.c */
int stringify(char* s, int digits, int divisor, int value, int shift);
void LittleEndian(char* start, int ByteMode);

struct entry
{
    struct entry* next;
    char* name;
};

FILE* output;
struct entry* jump_table;
int count;
char* entry;

void consume_token(FILE* source_file, char* s)
{
    int i = 0;
    int c = fgetc(source_file);
    require(EOF != c, "Can not have an EOF token\n");
    do
    {
        s[i] = c;
        i = i + 1;
        require(max_string > i, "Token exceeds token length restriction\n");
        c = fgetc(source_file);
        if(EOF == c) break;
    } while(!in_set(c, " \t\n>"));
}

void storeLabel(FILE* source_file)
{
    struct entry* entry = calloc(1, sizeof(struct entry));

    /* Prepend to list */
    entry->next = jump_table;
    jump_table = entry;

    /* Store string */
    entry->name = calloc((max_string + 1), sizeof(char));
    consume_token(source_file, entry->name);

    count = count + 1;
}

void line_Comment(FILE* source_file)
{
    int c = fgetc(source_file);
    while(!in_set(c, "\n\r"))
    {
        if(EOF == c) break;
        c = fgetc(source_file);
    }
}

void purge_string(FILE* source_file)
{
    int c = fgetc(source_file);
    while((EOF != c) && ('"' != c))
    {
        c = fgetc(source_file);
    }
}

void first_pass(struct entry* input)
{
    if(NULL == input) return;
    first_pass(input->next);

    FILE* source_file = fopen(input->name, "r");

    if(NULL == source_file)
    {
        fputs("The file: ", stderr);
        fputs(input->name, stderr);
        fputs(" can not be opened!\n", stderr);
        exit(EXIT_FAILURE);
    }

    int c;
    for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
    {
        /* Check for and deal with label */
        if(58 == c)
        {
            storeLabel(source_file);
        }
        /* Check for and deal with line comments */
        else if (c == '#' || c == ';')
        {
            line_Comment(source_file);
        }
        else if ('"' == c)
        {
            purge_string(source_file);
        }
    }
    fclose(source_file);
}

void output_string_table(struct entry* node)
{
    fputs("\n# Generated string table\n:ELF_str\n", output);
    fputs(zero_8, output);
    fputs("\t# NULL string\n", output);
    struct entry* i;
    for(i = node; NULL != i; i = i->next)
    {
        fputs(":ELF_str_", output);
        fputs(i->name, output);
        fputs("\t\"", output);
        fputs(i->name, output);
        fputs("\"\n", output);
    }
    fputs("# END Generated string table\n\n", output);
}

void output_symbol_table(struct entry* node)
{
    fputs("\n# Generated symbol table\n:ELF_sym\n# Required NULL symbol entry\n", output);
    if(64 == BITSIZE)
    {
        fputs(zero_32, output);
        fputs("\t# st_name\n", output);

        fputs(zero_8, output);
        fputs("\t# st_info\n", output);

        fputs(zero_8, output);
        fputs("\t# st_other\n", output);

        fputs(one_16, output);
        fputs("\t# st_shndx\n", output);

        fputs(zero_32, output);
        fputc(' ', output);
        fputs(zero_32, output);
        fputs("\t# st_value\n", output);

        fputs(zero_32, output);
        fputc(' ', output);
        fputs(zero_32, output);
        fputs("\t# st_size\n\n", output);
    }
    else
    {
        fputs(zero_32, output);
        fputs("\t# st_name\n", output);

        fputs(zero_32, output);
        fputs("\t# st_value\n", output);

        fputs(zero_32, output);
        fputs("\t# st_size\n", output);

        fputs(zero_8, output);
        fputs("\t# st_info\n", output);

        fputs(zero_8, output);
        fputs("\t# st_other\n", output);

        fputs(one_16, output);
        fputs("\t# st_shndx\n\n", output);
    }

    struct entry* i;
    for(i = node; NULL != i; i = i->next)
    {
        fputs("%ELF_str_", output);
        fputs(i->name, output);
        fputs(">ELF_str\t# st_name\n", output);

        if(64 == BITSIZE)
        {
            fputs(two_8, output);
            fputs("\t# st_info (FUNC)\n", output);

            if(('_' == i->name[0]) && !match(entry, i->name))
            {
                fputs(two_8, output);
                fputs("\t# st_other (hidden)\n", output);
            }
            else
            {
                fputs(zero_8, output);
                fputs("\t# st_other (other)\n", output);
            }

            fputs(one_16, output);
            fputs("\t# st_shndx\n", output);

            fputs("&", output);
            fputs(i->name, output);
            fputc(' ', output);
            fputs(zero_32, output);
            fputs("\t# st_value\n", output);

            fputs(zero_32, output);
            fputc(' ', output);
            fputs(zero_32, output);
            fputs("\t# st_size (unknown size)\n\n", output);
        }
        else
        {
            fputs("&", output);
            fputs(i->name, output);
            fputs("\t#st_value\n", output);

            fputs(zero_32, output);
            fputs("\t# st_size (unknown size)\n", output);

            fputs(two_8, output);
            fputs("\t# st_info (FUNC)\n", output);

            if(('_' == i->name[0]) && !match(entry, i->name))
            {
                fputs(two_8, output);
                fputs("\t# st_other (hidden)\n", output);
            }
            else
            {
                fputs(zero_8, output);
                fputs("\t# st_other (default)\n", output);
            }

            fputs(one_16, output);
            fputs("\t# st_shndx\n\n", output);
        }
    }

    fputs("# END Generated symbol table\n", output);
}

struct entry* reverse_list(struct entry* head)
{
    struct entry* root = NULL;
    struct entry* next;
    while(NULL != head)
    {
        next = head->next;
        head->next = root;
        root = head;
        head = next;
    }
    return root;
}

void write_int(char* field, char* label)
{
    fputs(field, output);
    fputs("\t#", output);
    fputs(label, output);
    fputc('\n', output);
}

void write_register(char* field, char* label)
{
    /* $field section in the section headers are different size for 32 and 64bits */
    /* The below is broken for BigEndian */
    fputs(field, output);
    if(64 == BITSIZE)
    {
        fputc(' ', output);
        fputs(zero_32, output);
    }

    fputs("\t#", output);
    fputs(label, output);
    fputc('\n', output);
}

void write_section(char* label, char* name, char* type, char* flags, char* address, char* offset, char* size, char* link, char* info, char* entry)
{
    /* Write label */
    fputc('\n', output);
    fputs(label, output);
    fputc('\n', output);

    write_int(name, "sh_name");
    write_int(type, "sh_type");
    write_register(flags, "sh_flags");
    write_register(address, "sh_addr");
    write_register(offset, "sh_offset");
    write_register(size, "sh_size");
    write_int(link, "sh_link");

    /* Deal with the ugly case of stubs */
    fputs(info, output);
    fputs("\t#sh_info\n", output);

    /* Alignment section in the section headers are different size for 32 and 64bits */
    /* The below is broken for BigEndian */
    if(64 == BITSIZE)
    {
        fputs(one_32, output);
        fputc(' ', output);
        fputs(zero_32, output);
        fputs("\t#sh_addralign\n", output);
    }
    else
    {
        fputs(one_32, output);
        fputs("\t#sh_addralign\n", output);
    }

    write_register(entry, "sh_entsize");
}

char* get_string(int value, int size, int ByteMode, int shift)
{
    char* ch = calloc(42, sizeof(char));
    require(NULL != ch, "Exhausted available memory\n");
    ch[0] = '\'';
    stringify(ch+1, size, ByteMode, value, shift);
    if(!BigEndian) LittleEndian(ch+1, ByteMode);
    int i = 0;
    while(0 != ch[i])
    {
        i = i + 1;
    }
    ch[i] = '\'';
    return ch;
}

char* setup_string(int value, int number_of_bytes, int ByteMode)
{
    int shift;
    int size;
    if(HEX == ByteMode)
    {
        size = 2;
        shift = 4;
    }
    else if(OCTAL == ByteMode)
    {
        size = 3;
        shift = 3;
    }
    else if(BINARY == ByteMode)
    {
        size = 8;
        shift = 1;
    }
    else
    {
        fputs("reached impossible mode\n", stderr);
        exit(EXIT_FAILURE);
    }

    return get_string(value, number_of_bytes *size, ByteMode, shift);
}

void setup_strings(int ByteMode)
{
    zero_8 = setup_string(0, 1, ByteMode);
    zero_16 = setup_string(0, 2, ByteMode);
    zero_32 = setup_string(0, 4, ByteMode);
    one_16 = setup_string(1, 2, ByteMode);
    one_32 = setup_string(1, 4, ByteMode);
    two_8 = setup_string(2, 1, ByteMode);
    two_32 = setup_string(2, 4, ByteMode);
    three_32 = setup_string(3, 4, ByteMode);
    six_32 = setup_string(6, 4, ByteMode);
    sixteen_32 = setup_string(16, 4, ByteMode);
    twentyfour_32 = setup_string(24, 4, ByteMode);
}

/* Standard C main program */
int main(int argc, char **argv)
{
    jump_table = NULL;
    struct entry* input = NULL;
    output = stdout;
    char* output_file = "";
    entry = "";
    BITSIZE = 32;
    count = 1;
    BigEndian = TRUE;
    int ByteMode = HEX;
    int set = FALSE;
    struct entry* temp;
    struct entry* head;

    int option_index = 1;
    while(option_index <= argc)
    {
        if(NULL == argv[option_index])
        {
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-h") || match(argv[option_index], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" --file FILENAME1 {--file FILENAME2} --output FILENAME\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "--64"))
        {
            BITSIZE = 64;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-f") || match(argv[option_index], "--file"))
        {
            temp = calloc(1, sizeof(struct entry));
            temp->name = argv[option_index + 1];
            temp->next = input;
            input = temp;
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-o") || match(argv[option_index], "--output"))
        {
            output_file = argv[option_index + 1];
            output = fopen(output_file, "w");

            if(NULL == output)
            {
                fputs("The file: ", stderr);
                fputs(input->name, stderr);
                fputs(" can not be opened!\n", stderr);
                exit(EXIT_FAILURE);
            }
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-b") || match(argv[option_index], "--binary"))
        {
            ByteMode = BINARY;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-O") || match(argv[option_index], "--octal"))
        {
            ByteMode = OCTAL;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-X") || match(argv[option_index], "--hex"))
        {
            ByteMode = HEX;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--big-endian"))
        {
            BigEndian = TRUE;
            set = TRUE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--little-endian"))
        {
            BigEndian = FALSE;
            set = TRUE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-V") || match(argv[option_index], "--version"))
        {
            fputs("blood-elf 2.0.1\n(Basically Launches Odd Object Dump ExecutabLe Files\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "--entry"))
        {
            head = calloc(1, sizeof(struct entry));
            /* Include _start or any other entry from your .hex2 */
            head->next = jump_table;
            jump_table = head;
            jump_table->name = argv[option_index + 1];
            /* However only the last one will be exempt from the _name hidden rule */
            entry = argv[option_index + 1];
            option_index = option_index + 2;
            count = count + 1;
        }
        else
        {
            fputs("Unknown option\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    /* Make sure we have a program tape to run */
    if (NULL == input)
    {
        return EXIT_FAILURE;
    }

    /* Force setting of endianness */
    if(!set)
    {
        fputs("either --little-endian or --big-endian MUST be set\n", stderr);
        return EXIT_FAILURE;
    }

    /* Setup the ugly formating because RISC-V sucks */
    setup_strings(ByteMode);

    /* Get all of the labels */
    first_pass(input);

    /* Reverse their order */
    jump_table = reverse_list(jump_table);

    /* Create sections */
    /* Create string names for sections */
    fputs("# Generated sections\n:ELF_shstr\n", output);
    fputs(zero_8, output);
    fputs("\t# NULL\n", output);
    fputs(":ELF_shstr__text\n\".text\"\n", output);
    fputs(":ELF_shstr__shstr\n\".shstrtab\"\n", output);
    fputs(":ELF_shstr__sym\n\".symtab\"\n", output);
    fputs(":ELF_shstr__str\n\".strtab\"\n", output);

    /* Create NULL section header as is required by the Spec. So dumb and waste of bytes*/
    write_section(":ELF_section_headers", zero_32, zero_32, zero_32, zero_32, zero_32, zero_32, zero_32, zero_32, zero_32);
    write_section(":ELF_section_header_text", "%ELF_shstr__text>ELF_shstr", one_32, six_32, "&ELF_text", "%ELF_text>ELF_base", "%ELF_data>ELF_text", zero_32, zero_32, zero_32);
    write_section(":ELF_section_header_shstr", "%ELF_shstr__shstr>ELF_shstr", three_32, zero_32, "&ELF_shstr", "%ELF_shstr>ELF_base", "%ELF_section_headers>ELF_shstr", zero_32, zero_32, zero_32);
    write_section(":ELF_section_header_str", "%ELF_shstr__str>ELF_shstr", three_32, zero_32, "&ELF_str", "%ELF_str>ELF_base", "%ELF_sym>ELF_str", zero_32, zero_32, zero_32);
    if(64 == BITSIZE) write_section(":ELF_section_header_sym", "%ELF_shstr__sym>ELF_shstr", two_32, zero_32, "&ELF_sym", "%ELF_sym>ELF_base", "%ELF_end>ELF_sym", three_32, setup_string(count, 4, ByteMode), twentyfour_32);
    else write_section(":ELF_section_header_sym", "%ELF_shstr__sym>ELF_shstr", two_32, zero_32, "&ELF_sym", "%ELF_sym>ELF_base", "%ELF_end>ELF_sym", three_32, setup_string(count, 4, ByteMode), sixteen_32);

    /* Create dwarf stubs needed for objdump -d to get function names */
    output_string_table(jump_table);
    output_symbol_table(jump_table);
    fputs("\n:ELF_end\n", output);

    /* Close output file */
    fclose(output);

    return EXIT_SUCCESS;
}

File /M2libc/x86/x86_defs.M1

Live-bootstrap source file is 'seed/stage0-posix/M2libc/x86/x86_defs.M1'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/x86/x86_defs.M1
## Copyright (C) 2017 Jeremiah Orians
## This file is part of M2-Planet.
##
## M2-Planet is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## M2-Planet is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.


DEFINE add_eax, 81C0
DEFINE add_ebp, 81C5
DEFINE add_ebx,eax 01C3
DEFINE add_eax,ebp 01E8
DEFINE add_eax,ebx 01D8
DEFINE and_eax,ebx 21D8
DEFINE call E8
DEFINE call_eax FFD0
DEFINE cmp 39C3
DEFINE cdq 99
DEFINE div_ebx F7F3
DEFINE idiv_ebx F7FB
DEFINE int CD
DEFINE je 0F84
DEFINE jne 0F85
DEFINE jmp E9
DEFINE lea_eax,[ebp+DWORD] 8D85
DEFINE lea_eax,[esp+DWORD] 8D8424
DEFINE lea_ebx,[esp+DWORD] 8D9C24
DEFINE lea_ecx,[esp+DWORD] 8D8C24
DEFINE lea_edx,[esp+DWORD] 8D9424
DEFINE lea_esi,[esp+DWORD] 8DB424
DEFINE lea_edi,[esp+DWORD] 8DBC24
DEFINE mov_eax,[esp+DWORD] 8B8424
DEFINE mov_eax,ebp 89E8
DEFINE mov_eax,ebx 89D8
DEFINE mov_eax,ebx 89D8
DEFINE mov_eax,edx 89D0
DEFINE mov_ebx,eax 89C3
DEFINE mov_ecx,eax 89C1
DEFINE mov_ecx,esp 89E1
DEFINE mov_edi,esp 89E7
DEFINE mov_ebp,edi 89fd
DEFINE mov_ebp,esp 89E5
DEFINE mov_eax, B8
DEFINE mov_ebx, BB
DEFINE mov_edx, BA
DEFINE mov_eax,[eax] 8B00
DEFINE mov_ebx,[ebx] 8B1B
DEFINE mov_ecx,[ecx] 8B09
DEFINE mov_edx,[edx] 8B12
DEFINE mov_esi,[esi] 8B36
DEFINE mov_edi,[edi] 8B3F
DEFINE mov_[ebx],al 8803
DEFINE mov_[ebx],ax 668903
DEFINE mov_[ebx],eax 8903
DEFINE movsx_eax,BYTE_PTR_[eax] 0FBE00
DEFINE movsx_ebx,BYTE_PTR_[ebx] 0FBE1B
DEFINE movsx_eax,WORD_PTR_[eax] 0FBF00
DEFINE movzx_eax,BYTE_PTR_[eax] 0FB600
DEFINE movzx_eax,WORD_PTR_[eax] 0FB700
DEFINE movzx_eax,al 0FB6C0
DEFINE mul_ebx F7E3
DEFINE imul_ebx F7EB
DEFINE NULL 00000000
DEFINE not_eax F7D0
DEFINE or_eax,ebx 09D8
DEFINE pop_eax 58
DEFINE pop_ebx 5B
DEFINE pop_ebp 5D
DEFINE pop_edi 5F
DEFINE push_eax 50
DEFINE push_ebx 53
DEFINE push_ebp 55
DEFINE push_edi 57
DEFINE ret C3
DEFINE sal_eax, C1E0
DEFINE sal_eax,cl D3E0
DEFINE shl_eax,cl D3E0
DEFINE sar_eax,cl D3F8
DEFINE shr_eax,cl D3E8
DEFINE seta_al 0F97C0
DEFINE setae_al 0F93C0
DEFINE setb_al 0F92C0
DEFINE setbe_al 0F96C0
DEFINE sete_al 0F94C0
DEFINE setle_al 0F9EC0
DEFINE setl_al 0F9CC0
DEFINE setge_al 0F9DC0
DEFINE setg_al 0F9FC0
DEFINE setne_al 0F95C0
DEFINE sub_ebx,eax 29C3
DEFINE test_eax,eax 85C0
DEFINE xchg_ebx,eax 93
DEFINE xor_eax,ebx 31D8

File /M2libc/x86/libc-core.M1

Live-bootstrap source file is 'seed/stage0-posix/M2libc/x86/libc-core.M1'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/x86/libc-core.M1
## Copyright (C) 2016 Jeremiah Orians
## This file is part of M2-Planet.
##
## M2-Planet is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## M2-Planet is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.

:_start

    mov_ebp,esp                 ; Protect esp

    ;; Prepare argv
    lea_eax,[ebp+DWORD] %4      ; ARGV_address = EBP + 4
    push_eax                    ; Put argv on the stack

    ;; Prepare envp
    mov_eax,ebp                 ; Address we need to load from
    mov_eax,[eax]               ; Get ARGC
    add_eax, %2                 ; OFFSET = ARGC + 2
    sal_eax, !2                 ; OFFSET = OFFSET * WORDSIZE
    add_eax,ebp                 ; ENVP_address = ESP + OFFSET
    push_eax                    ; Put envp on the stack

    ;; Stack offset
    add_ebp, %4                 ; Fix ebp

    ;; Perform the main loop
    call %FUNCTION_main
    push_eax                    ; Put return on stack
    push_eax                    ; so that _exit gets the value

    ;; Exit to kernel
:FUNCTION_exit
:FUNCTION__exit
    mov_ebx,eax                 ; Using the return code given by main
    mov_eax, %1                 ; Syscall exit
    int !0x80                   ; Exit with that code

File /M2libc/x86/ELF-x86.hex2

Live-bootstrap source file is 'seed/stage0-posix/M2libc/x86/ELF-x86.hex2'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/x86/ELF-x86.hex2
### Copyright (C) 2016 Jeremiah Orians
### Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
### This file is part of M2-Planet.
###
### M2-Planet is free software: you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation, either version 3 of the License, or
### (at your option) any later version.
###
### M2-Planet is distributed in the hope that it will be useful,
### but WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
### GNU General Public License for more details.
###
### You should have received a copy of the GNU General Public License
### along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.

### stage0's hex2 format
###    !<label>          1 byte relative
###    $<label>          2 byte address
###    @<label>          2 byte relative
###    &<label>          4 byte address
###    %<label>          4 byte relative

### if you wish to use this header, you need to add :ELF_end to the end of your
### M1 or hex2 files.

## ELF Header

:ELF_base
7F 45 4C 46                    # e_ident[EI_MAG0-3] ELF's magic number

01                             # e_ident[EI_CLASS] Indicating 32 bit
01                             # e_ident[EI_DATA] Indicating little endianness
01                             # e_ident[EI_VERSION] Indicating original elf

03                             # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                             # e_ident[EI_ABIVERSION] See above

00 00 00 00 00 00 00           # e_ident[EI_PAD]

02 00                          # e_type Indicating Executable
03 00                          # e_machine Indicating 386
01 00 00 00                    # e_version Indicating original elf

&_start                        # e_entry Address of the entry point
%ELF_program_headers>ELF_base  # e_phoff Address of program header table
00 00 00 00                    # e_shoff Address of section header table

00 00 00 00                    # e_flags

34 00                          # e_ehsize Indicating our 52 Byte header

20 00                          # e_phentsize size of a program header table
01 00                          # e_phnum number of entries in program table

00 00                          # e_shentsize size of a section header table
00 00                          # e_shnum number of entries in section table

00 00                          # e_shstrndx index of the section names


:ELF_program_headers
:ELF_program_header__text
01 00 00 00                    # ph_type: PT-LOAD = 1
00 00 00 00                    # ph_offset
&ELF_base                      # ph_vaddr
&ELF_base                      # ph_physaddr
%ELF_end>ELF_base              # ph_filesz
%ELF_end>ELF_base              # ph_memsz
07 00 00 00                    # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                    # ph_alignment

:ELF_text

File /mescc-tools/M1-macro.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools/M1-macro.c'.
URL: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=M1-macro.c;id=08d5a0679fa4dc00babe02306f1bc50703083389
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
 * This file is part of mescc-tools.
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "M2libc/bootstrappable.h"

/* Internal processing Constants */
// CONSTANT max_string 4096
#define max_string 4096
// CONSTANT STR 2
#define STR 2
// CONSTANT NEWLINE 3
#define NEWLINE 3

/* Unique code for each architecture */
// CONSTANT KNIGHT 0
#define KNIGHT 0
// CONSTANT X86 3
#define X86 0x03
// CONSTANT AMD64 62
#define AMD64 0x3E
// CONSTANT ARMV7L 40
#define ARMV7L 0x28
// CONSTANT AARM64 183
#define AARM64 0xB7
// CONSTANT PPC64LE 21
#define PPC64LE 0x15
// CONSTANT RISCV32 243
#define RISCV32 0xF3
// CONSTANT RISCV64 65779
#define RISCV64 0x100F3 /* Because RISC-V unlike all other architectures does get a seperate e_machine when changing from 32 to 64bit */


/* How do you want that output? */
// CONSTANT HEX 16
#define HEX 16
// CONSTANT OCTAL 8
#define OCTAL 8
// CONSTANT BINARY 2
#define BINARY 2

/* Imported from stringify.c */
int stringify(char* s, int digits, int divisor, int value, int shift);
void LittleEndian(char* start, int ByteMode);

struct blob
{
    struct blob* next;
    int type;
    char* Text;
    char* Expression;
    struct blob* hash_next;
};

struct Token
{
    struct Token* next;
    struct blob* contents;
    char* filename;
    int linenumber;
};

/* Globals */
FILE* source_file;
FILE* destination_file;
int BigEndian;
int ByteMode;
int Architecture;
int linenumber;
struct Token* token_list;
struct blob* blob_list;
struct blob* define_blob;
struct blob* newline_blob;
int blob_count;
char* SCRATCH;
struct blob** hash_table;

void line_error(char* filename, int linenumber)
{
    fputs(filename, stderr);
    fputs(":", stderr);
    fputs(int2str(linenumber,10, FALSE), stderr);
    fputs(" :", stderr);
}

void ClearScratch()
{
    int i = 0;
    int c = SCRATCH[i];
    while(0 != c)
    {
        SCRATCH[i] = 0;
        i = i + 1;
        c = SCRATCH[i];
    }
}

int GetHash(char* s)
{
    int i = 5381;
    while(0 != s[0])
    {
        i = (i << 5) + i + s[0];
        s = s + 1;
    }
    return i & 0xFFFF;
}

struct blob* FindBlob()
{
    int hash = GetHash(SCRATCH);
    struct blob* i = hash_table[hash];
    while(NULL != i)
    {
        if(match(SCRATCH, i->Text)) return i;
        i = i->hash_next;
    }

    return NULL;
}

void AddHash(struct blob* a, char* s)
{
    int i = GetHash(s);
    a->hash_next = hash_table[i];
    hash_table[i] = a;
}

void NewBlob(int size)
{
    blob_count = blob_count + 1;
    struct blob* a = calloc(1, sizeof(struct blob));
    require(NULL != a, "Exhausted available memory\n");
    a->Text = calloc(size + 1, sizeof(char));
    require(NULL != a->Text, "Exhausted available memory\n");

    int i = 0;
    while(i <= size)
    {
        a->Text[i] = SCRATCH[i];
        i = i + 1;
    }
    a->next = blob_list;
    blob_list = a;
    AddHash(a, SCRATCH);
}

struct Token* newToken(char* filename, int linenumber)
{
    struct Token* p;

    p = calloc (1, sizeof (struct Token));
    require(NULL != p, "Exhausted available memory\n");

    p->filename = filename;
    p->linenumber = linenumber;

    return p;
}

struct Token* reverse_list(struct Token* head)
{
    struct Token* root = NULL;
    struct Token* next;
    while(NULL != head)
    {
        next = head->next;
        head->next = root;
        root = head;
        head = next;
    }
    return root;
}

void purge_lineComment()
{
    int c = fgetc(source_file);
    while(!in_set(c, "\n\r"))
    {
        if(EOF == c) break;
        c = fgetc(source_file);
    }
}

struct Token* append_newline(struct Token* head, char* filename)
{
    linenumber = linenumber + 1;
    if(NULL == head) return NULL;
    if(NEWLINE == head->contents->type)
    {/* Don't waste whitespace*/
        return head;
    }

    struct Token* lf = newToken(filename, linenumber);
    lf->contents = newline_blob;
    lf->next = head;
    return lf;
}


struct Token* store_atom(struct Token* head, char c, char* filename)
{
    ClearScratch();
    int ch = c;
    int i = 0;
    do
    {
        SCRATCH[i] = ch;
        ch = fgetc(source_file);
        i = i + 1;
        if(i >= max_string)
        {
            fputs("storing atom of size larger than max_string\n", stderr);
            line_error(filename, linenumber);
            fputc('\n', stderr);
            exit(EXIT_FAILURE);
        }
        if(EOF == ch) break;
    } while (!in_set(ch, "\t\n "));

    head->contents = FindBlob();
    if(NULL == head->contents)
    {
        NewBlob(i);
        head->contents = blob_list;
    }

    if('\n' == ch)
    {
        return append_newline(head, filename);
    }
    return head;
}

struct blob* store_string(char c, char* filename)
{
    ClearScratch();

    int ch = c;
    int i = 0;
    do
    {
        SCRATCH[i] = ch;
        i = i + 1;
        if('\n' == ch) linenumber = linenumber + 1;
        ch = fgetc(source_file);
        require(EOF != ch, "Unmatched \"!\n");

        if(max_string == i)
        {
            line_error(filename, linenumber);
            fputs("String: ", stderr);
            fputs(SCRATCH, stderr);
            fputs(" exceeds max string size\n", stderr);
            exit(EXIT_FAILURE);
        }
    } while(ch != c);

    struct blob* a = FindBlob();
    if(NULL == a)
    {
        NewBlob(i);
        a = blob_list;
        a->type = STR;
    }

    return a;
}

struct Token* Tokenize_Line(struct Token* head, char* filename)
{
    int c;
    struct Token* p;
    linenumber = 1;

    do
    {
restart:
        c = fgetc(source_file);

        if(in_set(c, ";#"))
        {
            purge_lineComment();
            head = append_newline(head, filename);
            goto restart;
        }

        if(in_set(c, "\t "))
        {
            goto restart;
        }

        if('\n' == c)
        {
            head = append_newline(head, filename);
            goto restart;
        }

        if(EOF == c)
        {
            head = append_newline(head, filename);
            goto done;
        }

        p = newToken(filename, linenumber);
        p->next = head;
        if(in_set(c, "'\""))
        {
            p->contents = store_string(c, filename);
        }
        else
        {
            p = store_atom(p, c, filename);
        }

        head = p;
    } while(TRUE);
done:
    return head;
}

void line_macro(struct Token* p)
{
    struct Token* i;
    for(i = p; NULL != i; i = i->next)
    {
        if(define_blob == i->contents)
        {
            require(NULL != i->next, "Macro name must exist\n");
            require(NULL != i->next->next, "Macro value must exist\n");

            i->contents = newline_blob;

            if (STR == i->next->next->contents->type)
            {
                i->next->contents->Expression = i->next->next->contents->Text + 1;
            }
            else
            {
                i->next->contents->Expression = i->next->next->contents->Text;
            }
            i->next = i->next->next->next;
        }
    }
}

void hexify_string(struct blob* p)
{
    char* table = "0123456789ABCDEF";
    int i = strlen(p->Text);
    int size;

    if(HEX == ByteMode) size = (((i << 1) + i) + 12);
    else if(OCTAL == ByteMode) size = (i << 2) + 1;
    else if(BINARY == ByteMode) size = (i << 3) + i + 1;
    else size = 1;

    require(1 != size, "hexify_string lacked a valid bytemode\n");
    char* d = calloc(size, sizeof(char));
    require(NULL != d, "Exhausted available memory\n");
    p->Expression = d;
    char* S = p->Text;

    if((KNIGHT == Architecture) && (HEX == ByteMode))
    {
        i = (((((i - 1) >> 2) + 1) << 3) + i);
        while( 0 < i)
        {
            i = i - 1;
            d[i] = '0';
        }
    }

    if(HEX == ByteMode)
    {
        while(0 != S[0])
        {
            S = S + 1;
            d[0] = table[S[0] >> 4];
            d[1] = table[S[0] & 0xF];
            d[2] = ' ';
            d = d + 3;
        }
    }
    else if(OCTAL == ByteMode)
    {
        while(0 != S[0])
        {
            S = S + 1;
            d[0] = table[S[0] >> 6];
            d[1] = table[(S[0] >> 3) & 0x7];
            d[2] = table[S[0] & 0x7];
            d[3] = ' ';
            d = d + 4;
        }
    }
    else if(BINARY == ByteMode)
    {
        while(0 != S[0])
        {
            S = S + 1;
            d[0] = table[S[0] >> 7];
            d[1] = table[(S[0] >> 6) & 0x1];
            d[2] = table[(S[0] >> 5) & 0x1];
            d[3] = table[(S[0] >> 4) & 0x1];
            d[4] = table[(S[0] >> 3) & 0x1];
            d[5] = table[(S[0] >> 2) & 0x1];
            d[6] = table[(S[0] >> 1) & 0x1];
            d[7] = table[S[0] & 0x1];
            d[8] = ' ';
            d = d + 9;
        }
    }
}

void process_string(struct blob* p)
{
    struct blob* i;
    for(i = p; NULL != i; i = i->next)
    {
        if(STR == i->type)
        {
            if('\'' == i->Text[0])
            {
                i->Expression = i->Text + 1;
            }
            else if('"' == i->Text[0])
            {
                hexify_string(i);
            }
        }
    }
}

char* pad_nulls(int size, char* nil)
{
    if(0 == size) return nil;
    require(size > 0, "negative null padding not possible\n");
    if(HEX == ByteMode) size = size * 2;
    else if (OCTAL == ByteMode) size = size * 3;
    else if (BINARY == ByteMode) size = size * 8;

    char* s = calloc(size + 1, sizeof(char));
    require(NULL != s, "Exhausted available memory\n");

    int i = 0;
    while(i < size)
    {
        s[i] = '0';
        i = i + 1;
    }

    return s;
}

void preserve_other(struct blob* p)
{
    struct blob* i;
    char c;
    for(i = p; NULL != i; i = i->next)
    {
        if(NULL == i->Expression)
        {
            c = i->Text[0];

            if(in_set(c, "!@$~%&:^"))
            {
                i->Expression = i->Text;
            }
            else if('<' == c)
            {
                i->Expression = pad_nulls(strtoint(i->Text + 1), i->Text);
            }
        }
    }
}

void bound_values(int displacement, int number_of_bytes, int low, int high)
{
    if((high < displacement) || (displacement < low))
    {
        fputs("A displacement of ", stderr);
        fputs(int2str(displacement, 10, TRUE), stderr);
        fputs(" does not fit in ", stderr);
        fputs(int2str(number_of_bytes, 10, TRUE), stderr);
        fputs(" bytes\n", stderr);
        exit(EXIT_FAILURE);
    }
}

void range_check(int displacement, int number_of_bytes, int absolute)
{
    if(4 == number_of_bytes) return;
    else if(absolute && (3 == number_of_bytes))
    {
        bound_values(displacement, number_of_bytes, -8388609, 16777217);
        return;
    }
    else if(3 == number_of_bytes)
    {
        bound_values(displacement, number_of_bytes, -8388609, 8388608);
        return;
    }
    else if(absolute && (2 == number_of_bytes))
    {
        bound_values(displacement, number_of_bytes, -32769, 65536);
        return;
    }
    else if(2 == number_of_bytes)
    {
        bound_values(displacement, number_of_bytes, -32769, 32768);
        return;
    }
    else if(absolute && (1 == number_of_bytes))
    {
        bound_values(displacement, number_of_bytes, -1, 256);
        return;
    }
    else if(1 == number_of_bytes)
    {   /* work around current only signed bytes */
        bound_values(displacement, number_of_bytes, -129, 256);
        return;
    }

    fputs("Received an invalid number of bytes in range_check\n", stderr);
    exit(EXIT_FAILURE);
}

char* express_number(int value, char c)
{
    char* ch = calloc(42, sizeof(char));
    require(NULL != ch, "Exhausted available memory\n");
    int size;
    int number_of_bytes;
    int shift;
    int absolute = FALSE;
    if('!' == c) number_of_bytes = 1;
    else if('@' == c) number_of_bytes = 2;
    else if('$' == c)
    {
        number_of_bytes = 2;
        absolute = TRUE;
    }
    else if('~' == c) number_of_bytes = 3;
    else if('%' == c) number_of_bytes = 4;
    else if('&' == c)
    {
        number_of_bytes = 4;
        absolute = TRUE;
    }
    else
    {
        fputs("Given symbol ", stderr);
        fputc(c, stderr);
        fputs(" to express immediate value ", stderr);
        fputs(int2str(value, 10, TRUE), stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }

    range_check(value, number_of_bytes, absolute);

    /* don't truncate prior to range check for -1 behavior */
    if('!' == c) value = value & 0xFF;
    else if(('@' == c) || ('$' == c)) value = value & 0xFFFF;
    else if('~' == c) value = value & 0xFFFFFF;
    else if(('%' == c) || ('&' == c)) value = value & 0xFFFFFFFF;

    if(HEX == ByteMode)
    {
        size = number_of_bytes * 2;
        shift = 4;
    }
    else if(OCTAL == ByteMode)
    {
        size = number_of_bytes * 3;
        shift = 3;
    }
    else if(BINARY == ByteMode)
    {
        size = number_of_bytes * 8;
        shift = 1;
    }
    else
    {
        fputs("Got invalid ByteMode in express_number\n", stderr);
        exit(EXIT_FAILURE);
    }

    stringify(ch, size, ByteMode, value, shift);

    if(!BigEndian) LittleEndian(ch, ByteMode);
    return ch;
}

char* express_word(int value, char c)
{
    char* s = calloc(43, sizeof(char));
    s[0] = '.';
    char* ch = s + 1;
    require(NULL != ch, "Exhausted available memory\n");
    int size;
    int shift;
    int immediate;
    if('!' == c)
    {
        /* Corresponds to RISC-V I format */
        immediate = (value & 0xFFF) << 20;
    }
    else if('@' == c)
    {
        /* Corresponds to RISC-V S format */
        immediate = ((value & 0x1F) << 7) | ((value & 0xFE0) << (31 - 11));
    }
    else if('~' == c)
    {
        /* Corresponds with RISC-V U format */
        if ((value & 0xFFF) < 0x800)
        {
            immediate = value & 0xFFFFF000;
        }
        else
        {
            immediate = (value & 0xFFFFF000) + 0x1000;
        }
    }
    else if('%' == c)
    {
        /* provides an option for 32bit immediate constants */
        immediate = value & 0xFFFFFFFF;
        /* Drop the leading . */
        ch = s;
    }
    else
    {
        fputs("Given symbol ", stderr);
        fputc(c, stderr);
        fputs(" to express immediate value ", stderr);
        fputs(int2str(value, 10, TRUE), stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }

    if(HEX == ByteMode)
    {
        size = 4 * 2;
        shift = 4;
    }
    else if(OCTAL == ByteMode)
    {
        size = 4 * 3;
        shift = 3;
    }
    else if(BINARY == ByteMode)
    {
        size = 4 * 8;
        shift = 1;
    }
    else
    {
        fputs("Got invalid ByteMode in express_number\n", stderr);
        exit(EXIT_FAILURE);
    }

    stringify(ch, size, ByteMode, immediate, shift);

    if(!BigEndian) LittleEndian(ch, ByteMode);
    return s;
}

void eval_immediates(struct blob* p)
{
    struct blob* i;
    int value;
    for(i = p; NULL != i; i = i->next)
    {
        if(NEWLINE == i->type) continue;
        else if('<' == i->Text[0]) continue;
        else if(NULL == i->Expression)
        {
            if((X86 == Architecture) || (AMD64 == Architecture) || (ARMV7L == Architecture) || (AARM64 == Architecture) || (PPC64LE == Architecture))
            {
                if(in_set(i->Text[0], "%~@!&$"))
                {
                    value = strtoint(i->Text + 1);

                    if(('0' == i->Text[1]) || (0 != value))
                    {
                        i->Expression = express_number(value, i->Text[0]);
                    }
                }
            }
            else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
            {
                if(in_set(i->Text[0], "%~@!"))
                {
                    value = strtoint(i->Text + 1);

                    if(('0' == i->Text[1]) || (0 != value))
                    {
                        i->Expression = express_word(value, i->Text[0]);
                    }
                }
            }
            else if(KNIGHT == Architecture)
            {
                value = strtoint(i->Text);
                if(('0' == i->Text[0]) || (0 != value))
                {
                    if(value > 65536) continue;
                    else if(value > 32767) i->Expression = express_number(value, '$');
                    else i->Expression = express_number(value, '@');
                }
            }
            else
            {
                fputs("Unknown architecture received in eval_immediates\n", stderr);
                exit(EXIT_FAILURE);
            }
        }
    }
}

void print_hex(struct Token* p)
{
    struct Token* i;
    for(i = p; NULL != i; i = i->next)
    {
        if(NEWLINE == i->contents->type)
        {
            if(NULL == i->next) fputc('\n', destination_file);
            else if(NEWLINE != i->next->contents->type) fputc('\n', destination_file);
        }
        else if(NULL != i->contents->Expression)
        {
            fputs(i->contents->Expression, destination_file);
            if(NEWLINE != i->next->contents->type) fputc(' ', destination_file);
        }
        else
        {
            line_error(i->filename, i->linenumber);
            fputs("Received invalid other; ", stderr);
            fputs(i->contents->Text, stderr);
            fputs("\n", stderr);
            exit(EXIT_FAILURE);
        }
    }
}

/* Standard C main program */
int main(int argc, char **argv)
{
    BigEndian = TRUE;
    Architecture = KNIGHT;
    destination_file = stdout;
    ByteMode = HEX;
    char* filename;
    char* arch;
    blob_count = 2;
    hash_table = calloc(65537, sizeof(struct blob*));
    require(NULL != hash_table, "failed to allocate hash_table\n");

    /* Create newline blob */
    newline_blob = calloc(1, sizeof(struct blob));
    require(NULL != newline_blob, "failed to allocate newline_blob\n");
    newline_blob->Text = "\n";
    newline_blob->Expression = "\n";
    newline_blob->type = NEWLINE;
    AddHash(newline_blob, "\n");

    /* Start the blob list with DEFINE and newline */
    blob_list = calloc(1, sizeof(struct blob));
    require(NULL != blob_list, "failed to allocate DEFINE blob\n");
    blob_list->Text = "DEFINE";
    define_blob = blob_list;
    blob_list->next = newline_blob;
    AddHash(define_blob, "DEFINE");

    /* Initialize scratch */
    SCRATCH = calloc(max_string + 1, sizeof(char));
    require(NULL != SCRATCH, "failed to allocate SCRATCH buffer");

    int option_index = 1;
    while(option_index <= argc)
    {
        if(NULL == argv[option_index])
        {
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--big-endian"))
        {
            BigEndian = TRUE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--little-endian"))
        {
            BigEndian = FALSE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-A") || match(argv[option_index], "--architecture"))
        {
            arch = argv[option_index + 1];
            if(match("knight-native", arch) || match("knight-posix", arch)) Architecture = KNIGHT;
            else if(match("x86", arch)) Architecture = X86;
            else if(match("amd64", arch)) Architecture = AMD64;
            else if(match("armv7l", arch)) Architecture = ARMV7L;
            else if(match("aarch64", arch)) Architecture = AARM64;
            else if(match("ppc64le", arch)) Architecture = PPC64LE;
            else if(match("riscv32", arch)) Architecture = RISCV32;
            else if(match("riscv64", arch)) Architecture = RISCV64;
            else
            {
                fputs("Unknown architecture: ", stderr);
                fputs(arch, stderr);
                fputs(" know values are: knight-native, knight-posix, x86, amd64, armv7l, aarch64, ppc64le, riscv32 and riscv64", stderr);
                exit(EXIT_FAILURE);
            }
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-b") || match(argv[option_index], "--binary"))
        {
            ByteMode = BINARY;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-h") || match(argv[option_index], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" --file FILENAME1 {-f FILENAME2} (--big-endian|--little-endian) ", stderr);
            fputs("[--architecture name]\nArchitectures: knight-native, knight-posix, x86, amd64, armv7, riscv32 and riscv64\n", stderr);
            fputs("To leverage octal or binary output: --octal, --binary\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "-f") || match(argv[option_index], "--file"))
        {
            filename = argv[option_index + 1];
            source_file = fopen(filename, "r");

            if(NULL == source_file)
            {
                fputs("The file: ", stderr);
                fputs(argv[option_index + 1], stderr);
                fputs(" can not be opened!\n", stderr);
                exit(EXIT_FAILURE);
            }

            token_list = Tokenize_Line(token_list, filename);

            fclose(source_file);

            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-o") || match(argv[option_index], "--output"))
        {
            destination_file = fopen(argv[option_index + 1], "w");

            if(NULL == destination_file)
            {
                fputs("The file: ", stderr);
                fputs(argv[option_index + 1], stderr);
                fputs(" can not be opened!\n", stderr);
                exit(EXIT_FAILURE);
            }
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-O") || match(argv[option_index], "--octal"))
        {
            ByteMode = OCTAL;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-V") || match(argv[option_index], "--version"))
        {
            fputs("M1 1.5.0\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    if(NULL == token_list)
    {
        fputs("Either no input files were given or they were empty\n", stderr);
        exit(EXIT_FAILURE);
    }

    token_list = reverse_list(token_list);
    line_macro(token_list);
    process_string(blob_list);
    eval_immediates(blob_list);
    preserve_other(blob_list);
    print_hex(token_list);

    fclose(destination_file);

    return EXIT_SUCCESS;
}

File /M2libc/x86/ELF-x86-debug.hex2

Live-bootstrap source file is 'seed/stage0-posix/M2libc/x86/ELF-x86-debug.hex2'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/x86/ELF-x86-debug.hex2
### Copyright (C) 2016 Jeremiah Orians
### Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
### This file is part of M2-Planet.
###
### M2-Planet is free software: you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation, either version 3 of the License, or
### (at your option) any later version.
###
### M2-Planet is distributed in the hope that it will be useful,
### but WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
### GNU General Public License for more details.
###
### You should have received a copy of the GNU General Public License
### along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.

### stage0's hex2 format
###    !<label>          1 byte relative
###    $<label>          2 byte address
###    @<label>          2 byte relative
###    &<label>          4 byte address
###    %<label>          4 byte relative

### if you wish to use this header, you need to add :ELF_end to the end of your
### M1 or hex2 files.

## ELF Header

:ELF_base
7F 45 4C 46                    # e_ident[EI_MAG0-3] ELF's magic number

01                             # e_ident[EI_CLASS] Indicating 32 bit
01                             # e_ident[EI_DATA] Indicating little endianness
01                             # e_ident[EI_VERSION] Indicating original elf

03                             # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                             # e_ident[EI_ABIVERSION] See above

00 00 00 00 00 00 00           # e_ident[EI_PAD]

02 00                          # e_type Indicating Executable
03 00                          # e_machine Indicating 386
01 00 00 00                    # e_version Indicating original elf

&_start                        # e_entry Address of the entry point
%ELF_program_headers>ELF_base  # e_phoff Address of program header table
%ELF_section_headers>ELF_base  # e_shoff Address of section header table

00 00 00 00                    # e_flags

34 00                          # e_ehsize Indicating our 52 Byte header

20 00                          # e_phentsize size of a program header table
01 00                          # e_phnum number of entries in program table

28 00                          # e_shentsize size of a section header table
05 00                          # e_shnum number of entries in section table

02 00                          # e_shstrndx index of the section names


:ELF_program_headers
:ELF_program_header__text
01 00 00 00                    # ph_type: PT-LOAD = 1
00 00 00 00                    # ph_offset
&ELF_base                      # ph_vaddr
&ELF_base                      # ph_physaddr
%ELF_end>ELF_base              # ph_filesz
%ELF_end>ELF_base              # ph_memsz
07 00 00 00                    # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                    # ph_alignment

:ELF_text

File /M2libc/sys/types.h

Live-bootstrap source file is 'seed/stage0-posix/M2libc/sys/types.h'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/sys/types.h
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SYS_TYPES_H
#define _SYS_TYPES_H

#ifndef __M2__
#include "../gcc_req.h"
#endif

typedef SCM ulong;
typedef long ssize_t;
typedef int pid_t;
typedef long intptr_t;
typedef ulong uintptr_t;
typedef long clock_t;
typedef int mode_t;
typedef long dev_t;
#endif

File /M2libc/stddef.h

Live-bootstrap source file is 'seed/stage0-posix/M2libc/stddef.h'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/stddef.h
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _STDDEF_H
#define _STDDEF_H
#include <sys/types.h>

#define NULL 0
typedef long ptrdiff_t;
typedef ulong size_t;

#endif

File /M2libc/sys/utsname.h

Live-bootstrap source file is 'seed/stage0-posix/M2libc/sys/utsname.h'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/sys/utsname.h
/* Copyright (C) 2024 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _UTSNAME_H
#define _UTSNAME_H
struct utsname
{
    char sysname[65];    /* Operating system name (e.g., "Linux") */
    char nodename[65];   /* Name within "some implementation-defined network" */
    char release[65];    /* Operating system release (e.g., "2.6.28") */
    char version[65];    /* Operating system version */
    char machine[65];    /* Hardware identifier */
};

#ifdef __M2__
/* already exists in $ARCH/linux/unistd.c */
#else
int uname(struct utsname*);
#endif
#endif

File /M2libc/x86/linux/unistd.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/x86/linux/unistd.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/x86/linux/unistd.c
/* Copyright (C) 2020 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _UNISTD_C
#define _UNISTD_C
#include <sys/utsname.h>
#define NULL 0
#define __PATH_MAX 4096

void* malloc(unsigned size);

int access(char* pathname, int mode)
{
    asm("lea_ebx,[esp+DWORD] %8"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %4"
        "mov_ecx,[ecx]"
        "mov_eax, %33"
        "int !0x80");
}

int chdir(char* path)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %12"
        "int !0x80");
}

int fchdir(int fd)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %133"
        "int !0x80");
}

/* Defined in the libc */
void _exit(int value);

int fork()
{
    asm("mov_eax, %2"
        "mov_ebx, %0"
        "int !0x80");
}


int waitpid (int pid, int* status_ptr, int options)
{
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %7"
        "int !0x80");
}


int execve(char* file_name, char** argv, char** envp)
{
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %11"
        "int !0x80");
}

int read(int fd, char* buf, unsigned count) {
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %3"
        "int !0x80");
}

int write(int fd, char* buf, unsigned count) {
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %4"
        "int !0x80");
}

int lseek(int fd, int offset, int whence)
{
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %19"
        "int !0x80");
}

int close(int fd)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %6"
        "int !0x80");
}


int unlink (char *filename)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %10"
        "int !0x80");
}


int _getcwd(char* buf, int size)
{
    asm("lea_ebx,[esp+DWORD] %8"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %4"
        "mov_ecx,[ecx]"
        "mov_eax, %183"
        "int !0x80");
}


char* getcwd(char* buf, unsigned size)
{
    int c = _getcwd(buf, size);
    if(0 == c) return NULL;
    return buf;
}


char* getwd(char* buf)
{
    return getcwd(buf, __PATH_MAX);
}


char* get_current_dir_name()
{
    return getcwd(malloc(__PATH_MAX), __PATH_MAX);
}


int brk(void *addr)
{
    asm("mov_eax,[esp+DWORD] %4"
        "push_eax"
        "mov_eax, %45"
        "pop_ebx"
        "int !0x80");
}

int uname(struct utsname* unameData)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %109"
        "int !0x80");
}

int unshare(int flags)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %310"
        "int !0x80");
}

int geteuid()
{
    asm("mov_eax, %201"
        "int !0x80");
}

int getegid()
{
    asm("mov_eax, %202"
        "int !0x80");
}

int mount(char *source, char *target, char *filesystemtype, SCM mountflags, void *data)
{
    asm("lea_ebx,[esp+DWORD] %20"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %16"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %12"
        "mov_edx,[edx]"
        "lea_esi,[esp+DWORD] %8"
        "mov_esi,[esi]"
        "lea_edi,[esp+DWORD] %4"
        "mov_edi,[edi]"
        "mov_eax, %21"
        "int !0x80");
}

int chroot(char *path)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %61"
        "int !0x80");
}

#endif

File /M2libc/x86/linux/fcntl.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/x86/linux/fcntl.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/x86/linux/fcntl.c
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __FCNTL_C
#define __FCNTL_C

#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 00100
#define O_EXCL 00200
#define O_TRUNC 001000
#define O_APPEND 002000

#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_IRWXU 00700


int _open(char* name, int flag, int mode)
{
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %5"
        "int !0x80");
}

#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif

File /M2libc/fcntl.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/fcntl.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/fcntl.c
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _FCNTL_C
#define _FCNTL_C

#ifdef __M2__
#if __uefi__
#include <uefi/fcntl.c>
#elif __i386__
#include <x86/linux/fcntl.c>
#elif __x86_64__
#include <amd64/linux/fcntl.c>
#elif __arm__
#include <armv7l/linux/fcntl.c>
#elif __aarch64__
#include <aarch64/linux/fcntl.c>
#elif __riscv && __riscv_xlen==32
#include <riscv32/linux/fcntl.c>
#elif __riscv && __riscv_xlen==64
#include <riscv64/linux/fcntl.c>
#elif __knight_posix__
#include <knight/linux/fcntl.c>
#elif __knight__
#include <knight/native/fcntl.c>
#else
#error arch not supported
#endif
#else
extern int _open(char* name, int flag, int mode);
#endif

int errno;

int open(char* name, int flag, int mode)
{
    int fd = _open(name, flag, mode);
    if(0 > fd)
    {
        errno = -fd;
        fd = -1;
    }
    return fd;
}

#endif

File /M2libc/x86/linux/sys/stat.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/x86/linux/sys/stat.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/x86/linux/sys/stat.c
/* Copyright (C) 2020 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SYS_STAT_C
#define _SYS_STAT_C

#include <sys/types.h>

#define S_IRWXU 00700
#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400

#define S_ISUID 04000
#define S_ISGID 02000
#define S_IXGRP 00010
#define S_IXOTH 00001
#define S_IRGRP 00040
#define S_IROTH 00004
#define S_IWGRP 00020
#define S_IWOTH 00002
#define S_IRWXG 00070
#define S_IRWXO 00007


int chmod(char *pathname, int mode)
{
    asm("lea_ebx,[esp+DWORD] %8"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %4"
        "mov_ecx,[ecx]"
        "mov_eax, %15"
        "int !0x80");
}


int fchmod(int a, mode_t b)
{
    asm("lea_ebx,[esp+DWORD] %8"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %4"
        "mov_ecx,[ecx]"
        "mov_eax, %94"
        "int !0x80");
}


int mkdir(char const* a, mode_t b)
{
    asm("lea_ebx,[esp+DWORD] %8"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %4"
        "mov_ecx,[ecx]"
        "mov_eax, %39"
        "int !0x80");
}


int mknod(char const* a, mode_t b, dev_t c)
{
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %14"
        "int !0x80");
}


mode_t umask(mode_t m)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %60"
        "int !0x80");
}

#endif

File /M2libc/stdlib.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/stdlib.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/stdlib.c
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0

#define _IN_USE 1
#define _NOT_IN_USE 0

typedef char wchar_t;

void exit(int value);

struct _malloc_node
{
    struct _malloc_node *next;
    void* block;
    size_t size;
    int used;
};

struct _malloc_node* _allocated_list;
struct _malloc_node* _free_list;

/********************************
 * The core POSIX malloc        *
 ********************************/
long _malloc_ptr;
long _brk_ptr;
void* _malloc_brk(unsigned size)
{
    if(NULL == _brk_ptr)
    {
        _brk_ptr = brk(0);
        _malloc_ptr = _brk_ptr;
    }

    if(_brk_ptr < _malloc_ptr + size)
    {
        _brk_ptr = brk(_malloc_ptr + size);
        if(-1 == _brk_ptr) return 0;
    }

    long old_malloc = _malloc_ptr;
    _malloc_ptr = _malloc_ptr + size;
    return old_malloc;
}

void __init_malloc()
{
    _free_list = NULL;
    _allocated_list = NULL;
    return;
}

/************************************************************************
 * Handle with the tricky insert behaviors for our nodes                *
 * As free lists must be sorted from smallest to biggest to enable      *
 * cheap first fit logic                                                *
 * The free function however is rarely called, so it can kick sand and  *
 * do things the hard way                                               *
 ************************************************************************/
void _malloc_insert_block(struct _malloc_node* n, int used)
{
    /* Allocated block doesn't care about order */
    if(_IN_USE == used)
    {
        /* Literally just be done as fast as possible */
        n->next = _allocated_list;
        _allocated_list = n;
        return;
    }

    /* sanity check garbage */
    if(_NOT_IN_USE != used) exit(EXIT_FAILURE);
    if(_NOT_IN_USE != n->used) exit(EXIT_FAILURE);
    if(NULL != n->next) exit(EXIT_FAILURE);

    /* Free block really does care about order */
    struct _malloc_node* i = _free_list;
    struct _malloc_node* last = NULL;
    while(NULL != i)
    {
        /* sort smallest to largest */
        if(n->size <= i->size)
        {
            /* Connect */
            n->next = i;
            /* If smallest yet */
            if(NULL == last) _free_list = n;
            /* or just another average block */
            else last->next = n;
            return;
        }

        /* iterate */
        last = i;
        i = i->next;
    }

    /* looks like we are the only one */
    if(NULL == last) _free_list = n;
    /* or we are the biggest yet */
    else last->next = n;
}

/************************************************************************
 * We only mark a block as unused, we don't actually deallocate it here *
 * But rather shove it into our _free_list                              *
 ************************************************************************/
void free(void* ptr)
{
/* just in case someone needs to quickly turn it off */
#ifndef _MALLOC_DISABLE_FREE
    struct _malloc_node* i = _allocated_list;
    struct _malloc_node* last = NULL;

    /* walk the whole freaking list if needed to do so */
    while(NULL != i)
    {
        /* did we find it? */
        if(i->block == ptr)
        {
            /* detach the block */
            if(NULL == last) _allocated_list = i->next;
            /* in a way that doesn't break the allocated list */
            else last->next = i->next;

            /* insert into free'd list */
            i->used = _NOT_IN_USE;
            i->next = NULL;
            _malloc_insert_block(i, _NOT_IN_USE);
            return;
        }

        /* iterate */
        last = i;
        i = i->next;
    }

    /* we received a pointer to a block that wasn't allocated */
    /* Bail *HARD* because I don't want to cover this edge case */
    exit(EXIT_FAILURE);
#endif
    /* if free is disabled, there is nothing to do */
    return;
}

/************************************************************************
 * find if there is any "FREED" blocks big enough to sit on our memory  *
 * budget's face and ruin its life. Respectfully of course              *
 ************************************************************************/
void* _malloc_find_free(unsigned size)
{
    struct _malloc_node* i = _free_list;
    struct _malloc_node* last = NULL;
    /* Walk the whole list if need be */
    while(NULL != i)
    {
        /* see if anything in it is equal or bigger than what I need */
        if((_NOT_IN_USE == i->used) && (i->size > size))
        {
            /* disconnect from list ensuring we don't break free doing so */
            if(NULL == last) _free_list = i->next;
            else last->next = i->next;

            /* insert into allocated list */
            i->used = _IN_USE;
            i->next = NULL;
            _malloc_insert_block(i, _IN_USE);
            return i->block;
        }

        /* iterate (will loop forever if you get this wrong) */
        last = i;
        i = i->next;
    }

    /* Couldn't find anything big enough */
    return NULL;
}

/************************************************************************
 * Well we couldn't find any memory good enough to satisfy our needs so *
 * we are going to have to go beg for some memory on the street corner  *
 ************************************************************************/
void* _malloc_add_new(unsigned size)
{
    struct _malloc_node* n;
#ifdef __uefi__
    n = _malloc_uefi(sizeof(struct _malloc_node));
    /* Check if we were beaten */
    if(NULL == n) return NULL;
    n->block = _malloc_uefi(size);
#else
    n = _malloc_brk(sizeof(struct _malloc_node));
    /* Check if we were beaten */
    if(NULL == n) return NULL;
    n->block = _malloc_brk(size);
#endif
    /* check if we were robbed */
    if(NULL == n->block) return NULL;

    /* Looks like we made it home safely */
    n->size = size;
    n->next = NULL;
    n->used = _IN_USE;
    /* lets pop the cork and party */
    _malloc_insert_block(n, _IN_USE);
    return n->block;
}

/************************************************************************
 * Safely iterates over all malloc nodes and frees them                 *
 ************************************************************************/
void __malloc_node_iter(struct _malloc_node* node, FUNCTION _free)
{
    struct _malloc_node* current;
    while(node != NULL)
    {
        current = node;
        node = node->next;
        _free(current->block);
        _free(current);
    }
}

/************************************************************************
 * Runs a callback with all previously allocated nodes.                 *
 * This can be useful if operating system does not do any clean up.     *
 ************************************************************************/
void* _malloc_release_all(FUNCTION _free)
{
    __malloc_node_iter(_allocated_list, _free);
    __malloc_node_iter(_free_list, _free);
}

/************************************************************************
 * Provide a POSIX standardish malloc function to keep things working   *
 ************************************************************************/
void* malloc(unsigned size)
{
    /* skip allocating nothing */
    if(0 == size) return NULL;

    /* use one of the standard block sizes */
    size_t max = 1 << 30;
    size_t used = 256;
    while(used < size)
    {
        used = used << 1;

        /* fail big allocations */
        if(used > max) return NULL;
    }

    /* try the cabinets around the house */
    void* ptr = _malloc_find_free(used);

    /* looks like we need to get some more from the street corner */
    if(NULL == ptr)
    {
        ptr = _malloc_add_new(used);
    }

    /* hopefully you can handle NULL pointers, good luck */
    return ptr;
}

/************************************************************************
 * Provide a POSIX standardish memset function to keep things working   *
 ************************************************************************/
void* memset(void* ptr, int value, int num)
{
    char* s;
    /* basically walk the block 1 byte at a time and set it to any value you want */
    for(s = ptr; 0 < num; num = num - 1)
    {
        s[0] = value;
        s = s + 1;
    }

    return ptr;
}

/************************************************************************
 * Provide a POSIX standardish calloc function to keep things working   *
 ************************************************************************/
void* calloc(int count, int size)
{
    /* if things get allocated, we are good*/
    void* ret = malloc(count * size);
    /* otherwise good luck */
    if(NULL == ret) return NULL;
    memset(ret, 0, (count * size));
    return ret;
}


/* USED EXCLUSIVELY BY MKSTEMP */
void __set_name(char* s, int i)
{
    s[5] = '0' + (i % 10);
    i = i / 10;
    s[4] = '0' + (i % 10);
    i = i / 10;
    s[3] = '0' + (i % 10);
    i = i / 10;
    s[2] = '0' + (i % 10);
    i = i / 10;
    s[1] = '0' + (i % 10);
    i = i / 10;
    s[0] = '0' + i;
}

/************************************************************************
 * Provide a POSIX standardish mkstemp function to keep things working  *
 ************************************************************************/
int mkstemp(char *template)
{
    /* get length of template */
    int i = 0;
    while(0 != template[i]) i = i + 1;
    i = i - 1;

    /* String MUST be more than 6 characters in length */
    if(i < 6) return -1;

    /* Sanity check the string matches the template requirements */
    int count = 6;
    int c;
    while(count > 0)
    {
        c = template[i];
        /* last 6 chars must be X */
        if('X' != c) return -1;
        template[i] = '0';
        i = i - 1;
        count = count - 1;
    }

    int fd = -1;
    count = -1;
    /* open will return -17 or other values */
    while(0 > fd)
    {
        /* Just give up after the planet has blown up */
        if(9000 < count) return -1;

        /* Try up to 9000 unique filenames before stopping */
        count = count + 1;
        __set_name(template+i+1, count);

        /* Pray we can */
        fd = open(template, O_RDWR | O_CREAT | O_EXCL, 00600);
    }

    /* well that only took count many tries */
    return fd;
}

/************************************************************************
 * wcstombs - convert a wide-character string to a multibyte string     *
 * because seriously UEFI??? UTF-16 is a bad design choice but I guess  *
 * they were drinking pretty hard when they designed UEFI; it is DOS    *
 * but somehow they magically found ways of making it worse             *
 ************************************************************************/
size_t wcstombs(char* dest, char* src, size_t n)
{
    int i = 0;

    do
    {
        /* UTF-16 is 2bytes per char and that first byte maps good enough to ASCII */
        dest[i] = src[2 * i];
        if(dest[i] == 0)
        {
            break;
        }
        i = i + 1;
        n = n - 1;
    } while (n != 0);

    return i;
}

/************************************************************************
 * getenv - get an environmental variable                               *
 ************************************************************************/
size_t _strlen(char const* str)
{
    size_t i = 0;
    while(0 != str[i]) i = i + 1;
    return i;
}
int _strncmp(char const* lhs, char const* rhs, size_t count)
{
    size_t i = 0;
    while(count > i)
    {
        if(0 == lhs[i]) break;
        if(lhs[i] != rhs[i]) return lhs[i] - rhs[i];
        i = i + 1;
    }

    return 0;
}
char** _envp;
char* getenv (char const* name)
{
    char** p = _envp;
    char* q;
    int length = _strlen(name);

    while (p[0] != 0)
    {
        if(_strncmp(name, p[0], length) == 0)
        {
            q = p[0] + length;
            if(q[0] == '=')
                return q + 1;
        }
        p += sizeof(char**); /* M2 pointer arithemtic */
    }

    return 0;
}

/************************************************************************
 * setenv - set an environmental variable                               *
 ************************************************************************/
char* _strcpy(char* dest, char const* src)
{
    int i = 0;

    while (0 != src[i])
    {
        dest[i] = src[i];
        i = i + 1;
    }
    dest[i] = 0;

    return dest;
}

int setenv(char const *s, char const *v, int overwrite_p)
{
    char** p = _envp;
    int length = _strlen(s);
    char* q;

    while (p[0] != 0)
    {
        if (_strncmp (s, p[0], length) == 0)
        {
            q = p[0] + length;
            if (q[0] == '=')
                break;
        }
        p += sizeof(char**); /* M2 pointer arithemtic */
    }
    char *entry = malloc (length + _strlen(v) + 2);
    int end_p = p[0] == 0;
    p[0] = entry;
    _strcpy(entry, s);
    _strcpy(entry + length, "=");
    _strcpy(entry + length + 1, v);
    entry[length + _strlen(v) + 2] = 0;
    if (end_p != 0)
        p[1] = 0;

    return 0;
}

File /M2libc/stdio.h

Live-bootstrap source file is 'seed/stage0-posix/M2libc/stdio.h'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/stdio.h
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _STDIO_H
#define _STDIO_H

#ifdef __M2__

/* Actual format of FILE */
struct __IO_FILE
{
    int fd;
    int bufmode; /* O_RDONLY = 0, O_WRONLY = 1 */
    int bufpos;
    int file_pos;
    int buflen;
    char* buffer;
    struct __IO_FILE* next;
    struct __IO_FILE* prev;
};

/* Now give us the FILE we all love */
typedef struct __IO_FILE FILE;

#include <stdio.c>
#else

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

/* Required constants */
/* For file I/O*/
#define EOF -1
#define BUFSIZ 4096

/* For lseek */
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2

/* Actual format of FILE */
struct __IO_FILE
{
    int fd;
    int bufmode; /* 0 = no buffer, 1 = read, 2 = write */
    int bufpos;
    int buflen;
    char* buffer;
};

/* Now give us the FILE we all love */
typedef struct __IO_FILE FILE;

/* Required variables */
extern FILE* stdin;
extern FILE* stdout;
extern FILE* stderr;

/* Standard C functions */
/* Getting */
extern int fgetc(FILE* f);
extern int getchar();
extern char* fgets(char* str, int count, FILE* stream);
extern size_t fread( void* buffer, size_t size, size_t count, FILE* stream );

/* Putting */
extern void fputc(char s, FILE* f);
extern void putchar(char s);
extern int fputs(char const* str, FILE* stream);
extern int puts(char const* str);
extern size_t fwrite(void const* buffer, size_t size, size_t count, FILE* stream );

/* File management */
extern FILE* fopen(char const* filename, char const* mode);
extern int fclose(FILE* stream);
extern int fflush(FILE* stream);

/* File Positioning */
extern int ungetc(int ch, FILE* stream);
extern long ftell(FILE* stream);
extern int fseek(FILE* f, long offset, int whence);
extern void rewind(FILE* f);
#endif
#endif

File /M2libc/stdio.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/stdio.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/stdio.c
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

/* Required constants */
/* For file I/O*/
#define EOF 0xFFFFFFFF
#define BUFSIZ 0x1000

/* For lseek */
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2

/* Required variables */
FILE* stdin;
FILE* stdout;
FILE* stderr;
FILE* __list;

void __init_io()
{
    __list = NULL;
    stdin = calloc(1, sizeof(FILE));
    stdin->fd = STDIN_FILENO;
    stdin->bufmode = O_RDONLY;
    stdin->buflen = 1;
    stdin->buffer = calloc(2, sizeof(char));

    stdout = calloc(1, sizeof(FILE));
    stdout->fd = STDOUT_FILENO;
    stdout->bufmode = O_WRONLY;
    stdout->buflen = 512;
    stdout->buffer = calloc(514, sizeof(char));

    stderr = calloc(1, sizeof(FILE));
    stderr->fd = STDERR_FILENO;
    stderr->bufmode = O_WRONLY;
    stderr->buflen = 512;
    stderr->buffer = calloc(514, sizeof(char));
}


/* Flush all IO on exit */
int fflush(FILE* stream);
void __kill_io()
{
    fflush(stdout);
    fflush(stderr);
    while(NULL != __list)
    {
        fflush(__list);
        __list = __list->next;
    }
}

/* Standard C functions */
/* Getting */
int read(int fd, char* buf, unsigned count);
int fgetc(FILE* f)
{
    /* Only read on read buffers */
    if(O_WRONLY == f->bufmode) return EOF;

    /* Deal with stdin */
    if(STDIN_FILENO == f->fd)
    {
        f->bufpos = 0;
        int r = read(f->fd, f->buffer, 1);

        /* Catch special case of STDIN gets nothing (AN EOF) */
        if(0 == r) return EOF;
    }

    /* Catch EOF */
    if(f->buflen <= f->bufpos) return EOF;

    /* Deal with standard case */
    int ret = f->buffer[f->bufpos];
    f->bufpos = f->bufpos + 1;

    /* Ensure 0xFF doesn't return EOF */
    return (ret & 0xFF);
}

size_t fread( void* buffer, size_t size, size_t count, FILE* stream )
{
    if(0 == size) return 0;
    if(0 == count) return 0;

    long n = size + count - 1;
    char* p = buffer;
    long i;
    unsigned c;
    for(i = 0; i < n; i = i + 1)
    {
        c = fgetc(stream);
        if(EOF == c) return (i/size);
        p[i] = c;
    }

    return (i/size);
}

int getchar()
{
    return fgetc(stdin);
}


char* fgets(char* str, int count, FILE* stream)
{
    int i = 0;
    int ch;
    while(i < count)
    {
        ch = fgetc(stream);
        if(EOF == ch) break;

        str[i] = ch;
        i = i + 1;

        if('\n' == ch) break;
    }

    return str;
}

/* Putting */
void fputc(char s, FILE* f)
{
    /* Only write on write buffers */
    if(O_RDONLY == f->bufmode) return;

    /* Add to buffer */
    f->buffer[f->bufpos] = s;
    f->bufpos = f->bufpos + 1;

    /* Flush if full or '\n' */
    if(f->bufpos == f->buflen) fflush(f);
    else if(('\n' == s) && (2 >= f->fd)) fflush(f);
}

size_t fwrite(void const* buffer, size_t size, size_t count, FILE* stream )
{
    long n = size * count;
    if(0 == n) return 0;

    char* p = buffer;
    int c;
    long i;
    for(i=0; i < n; i = i + 1)
    {
        c = p[i];
        fputc(c, stream);
    }

    return (i/size);
}

void putchar(char s)
{
    fputc(s, stdout);
}


int fputs(char const* str, FILE* stream)
{
    while(0 != str[0])
    {
        fputc(str[0], stream);
        str = str + 1;
    }
    return 0;
}


int puts(char const* str)
{
    fputs(str, stdout);
    fputc('\n', stdout);
    return 0;
}


int lseek(int fd, int offset, int whence);
/* File management */
FILE* fopen(char const* filename, char const* mode)
{
    int f;
    FILE* fi = calloc(1, sizeof(FILE));
    fi->next = __list;
    if(NULL != __list) __list->prev = fi;
    __list = fi;
    int size;

    if('w' == mode[0]) f = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 00600);
    else f = open(filename, 0, 0); /* Everything else is a read */

    /* Negative numbers are error codes */
    if(0 > f)
    {
        return 0;
    }

    if('w' == mode[0])
    {
        /* Buffer as much as possible */
        fi->buffer = malloc(BUFSIZ * sizeof(char));
        fi->buflen = BUFSIZ;
        fi->bufmode = O_WRONLY;
    }
    else
    {
        /* Get enough buffer to read it all */
        size = lseek(f, 0, SEEK_END);
        fi->buffer = malloc((size + 1) * sizeof(char));
        fi->buflen = size;
        fi->bufmode = O_RDONLY;

        /* Now read it all */
        lseek(f, 0, SEEK_SET);
        read(f, fi->buffer, size);
    }

    fi->fd = f;
    return fi;
}

FILE* fdopen(int fd, char* mode)
{
    FILE* fi = calloc(1, sizeof(FILE));
    fi->next = __list;
    if(NULL != __list) __list->prev = fi;
    __list = fi;
    int size;

    if('w' == mode[0])
    {
        /* Buffer as much as possible */
        fi->buffer = malloc(BUFSIZ * sizeof(char));
        fi->buflen = BUFSIZ;
        fi->bufmode = O_WRONLY;
    }
    else
    {
        /* Get enough buffer to read it all */
        size = lseek(fd, 0, SEEK_END);
        fi->buffer = malloc((size + 1) * sizeof(char));
        fi->buflen = size;
        fi->bufmode = O_RDONLY;

        /* Now read it all */
        lseek(fd, 0, SEEK_SET);
        read(fd, fi->buffer, size);
    }

    fi->fd = fd;
    return fi;
}


int write(int fd, char* buf, unsigned count);
int fflush(FILE* stream)
{
    /* We only need to flush on writes */
    if(O_RDONLY == stream->bufmode) return 0;

    /* If nothing to flush */
    if(0 ==stream->bufpos) return 0;

    /* The actual flushing */
    int error = write(stream->fd, stream->buffer, stream->bufpos);

    /* Keep track of position */
    stream->file_pos = stream->file_pos + stream->bufpos;
    stream->bufpos = 0;

    return error;
}


int close(int fd);
int fclose(FILE* stream)
{
    /* Deal with STDIN, STDOUT and STDERR */
    /* No close for you */
    if(2 >= stream->fd) return 0;

    /* We only need to flush on writes */
    if(O_WRONLY == stream->bufmode)
    {
        fflush(stream);
    }

    /* Need to keep the File Descriptor for a moment */
    int fd = stream->fd;

    /* Remove from __list */
    if(NULL != stream->prev) stream->prev->next = stream->next;
    if(NULL != stream->next) stream->next->prev = stream->prev;
    /* Deal with special case of first node in __list */
    if (__list == stream) __list = __list->next;

    /* Free up the buffer and struct used for FILE */
    free(stream->buffer);
    free(stream);

    /* Do the actual closing */
    return close(fd);
}


int unlink(char* filename);
/* File Removal */
int remove(char *pathname)
{
    return unlink(pathname);
}


/* File Positioning */
int ungetc(int ch, FILE* stream)
{
    /* Deal with STDIN, STDOUT and STDERR */
    /* No ungetc for you */
    if(2 >= stream->fd) return EOF;

    /* You can't unget on a write stream! */
    if(O_WRONLY == stream->bufmode) return EOF;

    /* Don't underflow */
    if(0 == stream->bufpos) return EOF;

    /* Don't let crap be shoved into read stream */
    if(stream->buffer[stream->bufpos - 1] != ch) return EOF;

    stream->bufpos = stream->bufpos - 1;

    return ch;
}


long ftell(FILE* stream)
{
    /* Deal with STDIN, STDOUT and STDERR */
    /* No ftell for you */
    if(2 >= stream->fd) return 0;

    /* Deal with buffered output */
    if(O_WRONLY == stream->bufmode) return stream->file_pos + stream->bufpos;

    /* Deal with read */
    return stream->bufpos;
}


int fseek(FILE* f, long offset, int whence)
{
    /* Deal with STDIN, STDOUT and STDERR */
    /* No seek and destroy missions */
    if(2 >= f->fd) return 0;

    /* Deal with ugly case */
    if(O_WRONLY == f->bufmode)
    {
        fflush(f);
        return lseek(f->fd, offset, whence);
    }

    /* Deal with read mode */
    int pos;

    if(SEEK_SET == whence)
    {
        pos = offset;
    }
    else if(SEEK_CUR == whence)
    {
        pos = f->bufpos + offset;
    }
    else if(SEEK_END == whence)
    {
        pos = f->buflen + offset;
    }
    else return -1;

    if(pos < 0) return -1;
    if(pos > f->buflen) return -1;

    f->bufpos = pos;
    return pos;
}


void rewind(FILE* f)
{
    fseek(f, 0, SEEK_SET);
}

File /mescc-tools/hex2.h

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools/hex2.h'.
URL: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=hex2.h;id=08d5a0679fa4dc00babe02306f1bc50703083389
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
 * Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "M2libc/bootstrappable.h"

#define max_string 4096
#define TRUE 1
#define FALSE 0

#define KNIGHT 0
#define X86 0x03
#define AMD64 0x3E
#define ARMV7L 0x28
#define AARM64 0xB7
#define PPC64LE 0x15
#define RISCV32 0xF3
#define RISCV64 0x100F3 /* Because RISC-V unlike all other architectures does get a seperate e_machine when changing from 32 to 64bit */

#define HEX 16
#define OCTAL 8
#define BINARY 2


struct input_files
{
    struct input_files* next;
    char* filename;
};

struct entry
{
    struct entry* next;
    unsigned target;
    char* name;
};

File /mescc-tools/hex2_linker.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools/hex2_linker.c'.
URL: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=hex2_linker.c;id=08d5a0679fa4dc00babe02306f1bc50703083389
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
 * Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "hex2_globals.h"

/* Globals */
FILE* output;
struct entry** jump_tables;
int BigEndian;
int Base_Address;
int Architecture;
int ByteMode;
int exec_enable;
int ip;
char* scratch;
char* filename;
int linenumber;
int ALIGNED;

/* For processing bytes */
int hold;
int toggle;

void line_error()
{
    fputs(filename, stderr);
    fputs(":", stderr);
    fputs(int2str(linenumber, 10, FALSE), stderr);
    fputs(" :", stderr);
}

int consume_token(FILE* source_file)
{
    int i = 0;
    int c = fgetc(source_file);
    while(!in_set(c, " \t\n>"))
    {
        scratch[i] = c;
        i = i + 1;
        c = fgetc(source_file);
        require(max_string > i, "Consumed token exceeds length restriction\n");
        if(EOF == c) break;
    }

    return c;
}

int Throwaway_token(FILE* source_file)
{
    int c;
    do
    {
        c = fgetc(source_file);
        if(EOF == c) break;
    } while(!in_set(c, " \t\n>"));

    return c;
}

int length(char* s)
{
    int i = 0;
    while(0 != s[i]) i = i + 1;
    return i;
}

void Clear_Scratch(char* s)
{
    do
    {
        s[0] = 0;
        s = s + 1;
    } while(0 != s[0]);
}

void Copy_String(char* a, char* b)
{
    while(0 != a[0])
    {
        b[0] = a[0];
        a = a + 1;
        b = b + 1;
    }
}

int GetHash(char* s)
{
    int i = 5381;
    while(0 != s[0])
    {
        i = i * 31 + s[0];
        s = s + 1;
    }
    return (i & 0xFFFF);
}

unsigned GetTarget(char* c)
{
    struct entry* i;
    for(i = jump_tables[GetHash(c)]; NULL != i; i = i->next)
    {
        if(match(c, i->name))
        {
            return i->target;
        }
    }
    fputs("Target label ", stderr);
    fputs(c, stderr);
    fputs(" is not valid\n", stderr);
    exit(EXIT_FAILURE);
}

int storeLabel(FILE* source_file, int ip)
{
    struct entry* entry = calloc(1, sizeof(struct entry));
    require(NULL != entry, "failed to allocate entry\n");

    /* Ensure we have target address */
    entry->target = ip;

    /* Store string */
    int c = consume_token(source_file);
    entry->name = calloc(length(scratch) + 1, sizeof(char));
    require(NULL != entry->name, "failed to allocate entry->name\n");
    Copy_String(scratch, entry->name);
    Clear_Scratch(scratch);

    /* Prepend to list */
    int h = GetHash(entry->name);
    entry->next = jump_tables[h];
    jump_tables[h] = entry;

    return c;
}

void range_check(int displacement, int number_of_bytes, int absolute)
{
    if(4 == number_of_bytes) return;
    else if (absolute && (3 == number_of_bytes))
    {
        /* Deal with unsigned */
        if((16777215 < displacement) || (displacement < 0))
        {
            fputs("An absolute displacement of ", stderr);
            fputs(int2str(displacement, 10, TRUE), stderr);
            fputs(" does not fit in 3 bytes\n", stderr);
            exit(EXIT_FAILURE);
        }
        return;
    }
    else if (3 == number_of_bytes)
    {
        /* Deal with signed */
        if((8388607 < displacement) || (displacement < -8388608))
        {
            fputs("A relative displacement of ", stderr);
            fputs(int2str(displacement, 10, TRUE), stderr);
            fputs(" does not fit in 3 bytes\n", stderr);
            exit(EXIT_FAILURE);
        }
        return;
    }
    else if (absolute && (2 == number_of_bytes))
    {
        /* Deal with unsigned */
        if((65535 < displacement) || (displacement < 0))
        {
            fputs("An absolute displacement of ", stderr);
            fputs(int2str(displacement, 10, TRUE), stderr);
            fputs(" does not fit in 2 bytes\n", stderr);
            exit(EXIT_FAILURE);
        }
        return;
    }
    else if (2 == number_of_bytes)
    {
        /* Deal with signed */
        if((32767 < displacement) || (displacement < -32768))
        {
            fputs("A relative displacement of ", stderr);
            fputs(int2str(displacement, 10, TRUE), stderr);
            fputs(" does not fit in 2 bytes\n", stderr);
            exit(EXIT_FAILURE);
        }
        return;
    }
    else if (absolute && (1 == number_of_bytes))
    {
        /* Deal with unsigned */
        if((255 < displacement) || (displacement < 0))
        {
            fputs("An absolute displacement of ", stderr);
            fputs(int2str(displacement, 10, TRUE), stderr);
            fputs(" does not fit in 1 byte\n", stderr);
            exit(EXIT_FAILURE);
        }
        return;
    }
    else if (1 == number_of_bytes)
    {
        /* Deal with signed */
        if((127 < displacement) || (displacement < -128))
        {
            fputs("A relative displacement of ", stderr);
            fputs(int2str(displacement, 10, TRUE), stderr);
            fputs(" does not fit in 1 byte\n", stderr);
            exit(EXIT_FAILURE);
        }
        return;
    }

    fputs("Invalid number of bytes given\n", stderr);
    exit(EXIT_FAILURE);
}

void outputPointer(int displacement, int number_of_bytes, int absolute)
{
    unsigned value = displacement;

    /* HALT HARD if we are going to do something BAD*/
    range_check(displacement, number_of_bytes, absolute);

    if(BigEndian)
    { /* Deal with BigEndian */
        if(4 == number_of_bytes) fputc((value >> 24), output);
        if(3 <= number_of_bytes) fputc(((value >> 16)%256), output);
        if(2 <= number_of_bytes) fputc(((value >> 8)%256), output);
        if(1 <= number_of_bytes) fputc((value % 256), output);
    }
    else
    { /* Deal with LittleEndian */
        unsigned byte;
        while(number_of_bytes > 0)
        {
            byte = value % 256;
            value = value / 256;
            fputc(byte, output);
            number_of_bytes = number_of_bytes - 1;
        }
    }
}

int Architectural_displacement(int target, int base)
{
    if(KNIGHT == Architecture) return (target - base);
    else if(X86 == Architecture) return (target - base);
    else if(AMD64 == Architecture) return (target - base);
    else if(ALIGNED && (ARMV7L == Architecture))
    {
        ALIGNED = FALSE;
        /* Note: Branch displacements on ARM are in number of instructions to skip, basically. */
        if (target & 3)
        {
            line_error();
            fputs("error: Unaligned branch target: ", stderr);
            fputs(scratch, stderr);
            fputs(", aborting\n", stderr);
            exit(EXIT_FAILURE);
        }
        /*
         * The "fetch" stage already moved forward by 8 from the
         * beginning of the instruction because it is already
         * prefetching the next instruction.
         * Compensate for it by subtracting the space for
         * two instructions (including the branch instruction).
         * and the size of the aligned immediate.
         */
        return (((target - base + (base & 3)) >> 2) - 2);
    }
    else if(ARMV7L == Architecture)
    {
        /*
         * The size of the offset is 8 according to the spec but that value is
         * based on the end of the immediate, which the documentation gets wrong
         * and needs to be adjusted to the size of the immediate.
         * Eg 1byte immediate => -8 + 1 = -7
         */
        return ((target - base) - 8 + (3 & base));
    }
    else if(ALIGNED && (AARM64 == Architecture))
    {
            ALIGNED = FALSE;
            return (target - (~3 & base)) >> 2;
    }
    else if (AARM64 == Architecture)
    {
        return ((target - base) - 8 + (3 & base));
    }
    else if(ALIGNED && (PPC64LE == Architecture))
    {
        ALIGNED = FALSE;
        /* set Link register with branch */
        return (target - (base & 0xFFFFFFFC )) | 1;
    }
    else if(PPC64LE == Architecture)
    {
        /* DO *NOT* set link register with branch */
        return (target - (base & 0xFFFFFFFC));
    }
    else if(RISCV32 == Architecture || RISCV64 == Architecture) return (target - base);

    fputs("Unknown Architecture, aborting before harm is done\n", stderr);
    exit(EXIT_FAILURE);
}

void Update_Pointer(char ch)
{
    /* Calculate pointer size*/
    if(in_set(ch, "%&")) ip = ip + 4; /* Deal with % and & */
    else if(in_set(ch, "@$")) ip = ip + 2; /* Deal with @ and $ */
    else if('~' == ch) ip = ip + 3; /* Deal with ~ */
    else if('!' == ch) ip = ip + 1; /* Deal with ! */
    else
    {
        line_error();
        fputs("storePointer given unknown\n", stderr);
        exit(EXIT_FAILURE);
    }
}

void storePointer(char ch, FILE* source_file)
{
    /* Get string of pointer */
    Clear_Scratch(scratch);
    Update_Pointer(ch);
    int base_sep_p = consume_token(source_file);

    /* Lookup token */
    int target = GetTarget(scratch);
    int displacement;

    int base = ip;

    /* Change relative base address to :<base> */
    if ('>' == base_sep_p)
    {
        Clear_Scratch(scratch);
        consume_token (source_file);
        base = GetTarget (scratch);

        /* Force universality of behavior */
        displacement = (target - base);
    }
    else
    {
        displacement = Architectural_displacement(target, base);
    }

    /* output calculated difference */
    if('!' == ch) outputPointer(displacement, 1, FALSE); /* Deal with ! */
    else if('$' == ch) outputPointer(target, 2, TRUE); /* Deal with $ */
    else if('@' == ch) outputPointer(displacement, 2, FALSE); /* Deal with @ */
    else if('~' == ch) outputPointer(displacement, 3, FALSE); /* Deal with ~ */
    else if('&' == ch) outputPointer(target, 4, TRUE); /* Deal with & */
    else if('%' == ch) outputPointer(displacement, 4, FALSE);  /* Deal with % */
    else
    {
        line_error();
        fputs("error: storePointer reached impossible case: ch=", stderr);
        fputc(ch, stderr);
        fputs("\n", stderr);
        exit(EXIT_FAILURE);
    }
}

void line_Comment(FILE* source_file)
{
    int c = fgetc(source_file);
    while(!in_set(c, "\n\r"))
    {
        if(EOF == c) break;
        c = fgetc(source_file);
    }
    linenumber = linenumber + 1;
}

int hex(int c, FILE* source_file)
{
    if (in_set(c, "0123456789")) return (c - 48);
    else if (in_set(c, "abcdef")) return (c - 87);
    else if (in_set(c, "ABCDEF")) return (c - 55);
    else if (in_set(c, "#;")) line_Comment(source_file);
    else if ('\n' == c) linenumber = linenumber + 1;
    return -1;
}

int octal(int c, FILE* source_file)
{
    if (in_set(c, "01234567")) return (c - 48);
    else if (in_set(c, "#;")) line_Comment(source_file);
    else if ('\n' == c) linenumber = linenumber + 1;
    return -1;
}

int binary(int c, FILE* source_file)
{
    if (in_set(c, "01")) return (c - 48);
    else if (in_set(c, "#;")) line_Comment(source_file);
    else if ('\n' == c) linenumber = linenumber + 1;
    return -1;
}

void process_byte(char c, FILE* source_file, int write)
{
    if(HEX == ByteMode)
    {
        if(0 <= hex(c, source_file))
        {
            if(toggle)
            {
                if(write) fputc(((hold * 16)) + hex(c, source_file), output);
                ip = ip + 1;
                hold = 0;
            }
            else
            {
                hold = hex(c, source_file);
            }
            toggle = !toggle;
        }
    }
    else if(OCTAL ==ByteMode)
    {
        if(0 <= octal(c, source_file))
        {
            if(2 == toggle)
            {
                if(write) fputc(((hold * 8)) + octal(c, source_file), output);
                ip = ip + 1;
                hold = 0;
                toggle = 0;
            }
            else if(1 == toggle)
            {
                hold = ((hold * 8) + octal(c, source_file));
                toggle = 2;
            }
            else
            {
                hold = octal(c, source_file);
                toggle = 1;
            }
        }
    }
    else if(BINARY == ByteMode)
    {
        if(0 <= binary(c, source_file))
        {
            if(7 == toggle)
            {
                if(write) fputc((hold * 2) + binary(c, source_file), output);
                ip = ip + 1;
                hold = 0;
                toggle = 0;
            }
            else
            {
                hold = ((hold * 2) + binary(c, source_file));
                toggle = toggle + 1;
            }
        }
    }
}

void pad_to_align(int write)
{
    if((ARMV7L == Architecture) || (AARM64 == Architecture) || (RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        if(1 == (ip & 0x1))
        {
            ip = ip + 1;
            if(write) fputc('\0', output);
        }
        if(2 == (ip & 0x2))
        {
            ip = ip + 2;
            if(write)
            {
                fputc('\0', output);
                fputc('\0', output);
            }
        }
    }
}

void first_pass(struct input_files* input)
{
    if(NULL == input) return;
    first_pass(input->next);
    filename = input->filename;
    linenumber = 1;
    FILE* source_file = fopen(filename, "r");

    if(NULL == source_file)
    {
        fputs("The file: ", stderr);
        fputs(input->filename, stderr);
        fputs(" can not be opened!\n", stderr);
        exit(EXIT_FAILURE);
    }

    toggle = FALSE;
    int c;
    for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
    {
        /* Check for and deal with label */
        if(':' == c)
        {
            c = storeLabel(source_file, ip);
        }

        /* check for and deal with relative/absolute pointers to labels */
        if(in_set(c, "!@$~%&"))
        { /* deal with 1byte pointer !; 2byte pointers (@ and $); 3byte pointers ~; 4byte pointers (% and &) */
            Update_Pointer(c);
            c = Throwaway_token(source_file);
            if ('>' == c)
            { /* deal with label>base */
                c = Throwaway_token(source_file);
            }
        }
        else if('<' == c)
        {
            pad_to_align(FALSE);
        }
        else if('^' == c)
        {
            /* Just ignore */
            continue;
        }
        else process_byte(c, source_file, FALSE);
    }
    fclose(source_file);
}

void second_pass(struct input_files* input)
{
    if(NULL == input) return;
    second_pass(input->next);
    filename = input->filename;
    linenumber = 1;
    FILE* source_file = fopen(filename, "r");

    /* Something that should never happen */
    if(NULL == source_file)
    {
        fputs("The file: ", stderr);
        fputs(input->filename, stderr);
        fputs(" can not be opened!\nWTF-pass2\n", stderr);
        exit(EXIT_FAILURE);
    }

    toggle = FALSE;
    hold = 0;

    int c;
    for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
    {
        if(':' == c) c = Throwaway_token(source_file); /* Deal with : */
        else if(in_set(c, "!@$~%&")) storePointer(c, source_file);  /* Deal with !, @, $, ~, % and & */
        else if('<' == c) pad_to_align(TRUE);
        else if('^' == c) ALIGNED = TRUE;
        else process_byte(c, source_file, TRUE);
    }

    fclose(source_file);
}

File /mescc-tools/hex2_word.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools/hex2_word.c'.
URL: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=hex2_word.c;id=08d5a0679fa4dc00babe02306f1bc50703083389
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
 * Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "hex2_globals.h"

unsigned shiftregister;
unsigned tempword;
int updates;

void outOfRange(char* s, int value)
{
    line_error();
    fputs("error: value ", stderr);
    fputs(int2str(value, 10, TRUE), stderr);
    fputs(" out of range for field type ", stderr);
    fputs(s, stderr);
    fputs("\n", stderr);
    exit(EXIT_FAILURE);
}

void UpdateShiftRegister(char ch, int value)
{
    if ('.' == ch)
    {
        unsigned swap;
        /* Assume the user knows what they are doing */
        if(!BigEndian)
        {
            /* Swap from big-endian to little endian order */
            swap = (((value >> 24) & 0xFF) |
                    ((value << 8) & 0xFF0000) |
                    ((value >> 8) & 0xFF00) |
                    ((value & 0xFF) << 24));
        }
        else
        {
            /* Big endian needs no change */
            swap = value;
        }
        /* we just take the 4 bytes after the . and shove in the shift register */
        swap = swap & ((0xFFFF << 16) | 0xFFFF);
        shiftregister = shiftregister ^ swap;
    }
    else if ('!' == ch)
    {
        /* Corresponds to RISC-V I format */
        /* Will need architecture specific logic if more architectures go this route */
        /* no range check because it needs to work with labels for lui/addi + AUIPC combos */
        /* !label is used in the second instruction of AUIPC combo but we want an offset from */
        /* the first instruction */
        value = value + 4;
        tempword = (value & 0xFFF) << 20;
        /* Update shift register */
        tempword = tempword & ((0xFFFF << 16) | 0xFFFF);
        shiftregister = shiftregister ^ tempword;
    }
    else if ('@' == ch)
    {
        /* Corresponds to RISC-V B format (formerly known as SB) */
        /* Will need architecture specific logic if more architectures go this route */
        if ((value < -0x1000 || value > 0xFFF) || (value & 1)) outOfRange("B", value);

        /* Prepare the immediate's word */
        tempword = ((value & 0x1E) << 7)
            | ((value & 0x7E0) << (31 - 11))
            | ((value & 0x800) >> 4)
            | ((value & 0x1000) << (31 - 12));
        tempword = tempword & ((0xFFFF << 16) | 0xFFFF);
        /* Update shift register */
        shiftregister = shiftregister ^ tempword;
    }
    else if ('$' == ch)
    {
        /* Corresponds with RISC-V J format (formerly known as UJ) */
        /* Will need architecture specific logic if more architectures go this route */
        if ((value < -0x100000 || value > 0xFFFFF) || (value & 1)) outOfRange("J", value);

        tempword = ((value & 0x7FE) << (30 - 10))
            | ((value & 0x800) << (20 - 11))
            | ((value & 0xFF000))
            | ((value & 0x100000) << (31 - 20));
        tempword = tempword & ((0xFFFF << 16) | 0xFFFF);
        shiftregister = shiftregister ^ tempword;
    }
    else if ('~' == ch)
    {
        /* Corresponds with RISC-V U format */
        /* Will need architecture specific logic if more architectures go this route */
        if ((value & 0xFFF) < 0x800) tempword = value & (0xFFFFF << 12);
        else tempword = (value & (0xFFFFF << 12)) + 0x1000;
        tempword = tempword & ((0xFFFF << 16) | 0xFFFF);
        shiftregister = shiftregister ^ tempword;
    }
    else
    {
        line_error();
        fputs("error: UpdateShiftRegister reached impossible case: ch=", stderr);
        fputc(ch, stderr);
        fputs("\n", stderr);
        exit(EXIT_FAILURE);
    }
}

void WordStorePointer(char ch, FILE* source_file)
{
    /* Get string of pointer */
    ip = ip + 4;
    Clear_Scratch(scratch);
    int base_sep_p = consume_token(source_file);

    /* Lookup token */
    int target = GetTarget(scratch);
    int displacement;

    int base = ip;

    /* Change relative base address to :<base> */
    if ('>' == base_sep_p)
    {
        Clear_Scratch(scratch);
        consume_token (source_file);
        base = GetTarget (scratch);

        /* Force universality of behavior */
        displacement = (target - base);
    }
    else
    {
        displacement = Architectural_displacement(target, base);
    }

    /* output calculated difference */
    if('&' == ch) outputPointer(target, 4, TRUE); /* Deal with & */
    else if('%' == ch) outputPointer(displacement, 4, FALSE);  /* Deal with % */
    else
    {
        line_error();
        fputs("error: WordStorePointer reached impossible case: ch=", stderr);
        fputc(ch, stderr);
        fputs("\n", stderr);
        exit(EXIT_FAILURE);
    }
}

unsigned sr_nextb()
{
    unsigned rv = shiftregister & 0xff;
    shiftregister = shiftregister >> 8;
    return rv;
}

void DoByte(char c, FILE* source_file, int write, int update)
{
    if(HEX == ByteMode)
    {
        if(0 <= hex(c, source_file))
        {
            if(toggle)
            {
                if(write) fputc(((hold * 16)) + hex(c, source_file) ^ sr_nextb(), output);
                ip = ip + 1;
                if(update)
                {
                    hold = (hold * 16) + hex(c, source_file);
                    tempword = (tempword << 8) ^ hold;
                    updates = updates + 1;
                }
                hold = 0;
            }
            else
            {
                hold = hex(c, source_file);
            }
            toggle = !toggle;
        }
    }
    else if(OCTAL ==ByteMode)
    {
        if(0 <= octal(c, source_file))
        {
            if(2 == toggle)
            {
                if(write) fputc(((hold * 8)) + octal(c, source_file) ^ sr_nextb(), output);
                ip = ip + 1;
                if(update)
                {
                    hold = ((hold * 8) + octal(c, source_file));
                    tempword = (tempword << 8) ^ hold;
                    updates = updates + 1;
                }
                hold = 0;
                toggle = 0;
            }
            else if(1 == toggle)
            {
                hold = ((hold * 8) + octal(c, source_file));
                toggle = 2;
            }
            else
            {
                hold = octal(c, source_file);
                toggle = 1;
            }
        }
    }
    else if(BINARY == ByteMode)
    {
        if(0 <= binary(c, source_file))
        {
            if(7 == toggle)
            {
                if(write) fputc((hold * 2) + binary(c, source_file) ^ sr_nextb(), output);
                ip = ip + 1;
                if(update)
                {
                    hold = ((hold * 2) + binary(c, source_file));
                    tempword = (tempword << 8) ^ hold;
                    updates = updates + 1;
                }
                hold = 0;
                toggle = 0;
            }
            else
            {
                hold = ((hold * 2) + binary(c, source_file));
                toggle = toggle + 1;
            }
        }
    }
}

void WordFirstPass(struct input_files* input)
{
    if(NULL == input) return;
    WordFirstPass(input->next);
    filename = input->filename;
    linenumber = 1;
    FILE* source_file = fopen(filename, "r");

    if(NULL == source_file)
    {
        fputs("The file: ", stderr);
        fputs(input->filename, stderr);
        fputs(" can not be opened!\n", stderr);
        exit(EXIT_FAILURE);
    }

    toggle = FALSE;
    int c;
    for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
    {
        /* Check for and deal with label */
        if(':' == c)
        {
            c = storeLabel(source_file, ip);
        }

        /* check for and deal with relative/absolute pointers to labels */
        if('.' == c)
        {
            /* Read architecture specific number of bytes for what is defined as a word */
            /* 4bytes in RISC-V's case */
            updates = 0;
            tempword = 0;
            while (updates < 4)
            {
                c = fgetc(source_file);
                DoByte(c, source_file, FALSE, TRUE);
            }
            ip = ip - 4;
        }
        else if(in_set(c, "!@$~"))
        {
            /* Don't update IP */
            c = Throwaway_token(source_file);
        }
        else if(in_set(c, "%&"))
        {
            ip = ip + 4;
            c = Throwaway_token(source_file);
            if ('>' == c)
            { /* deal with label>base */
                c = Throwaway_token(source_file);
            }
        }
        else if('<' == c)
        {
            pad_to_align(FALSE);
        }
        else if('^' == c)
        {
            /* Just ignore */
            continue;
        }
        else DoByte(c, source_file, FALSE, FALSE);
    }
    fclose(source_file);
}

void WordSecondPass(struct input_files* input)
{
    shiftregister = 0;
    tempword = 0;

    if(NULL == input) return;
    WordSecondPass(input->next);
    filename = input->filename;
    linenumber = 1;
    FILE* source_file = fopen(filename, "r");

    /* Something that should never happen */
    if(NULL == source_file)
    {
        fputs("The file: ", stderr);
        fputs(input->filename, stderr);
        fputs(" can not be opened!\nWTF-pass2\n", stderr);
        exit(EXIT_FAILURE);
    }

    toggle = FALSE;
    hold = 0;

    int c;
    for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
    {
        if(':' == c) c = Throwaway_token(source_file); /* Deal with : */
        else if('.' == c)
        {
            /* Read architecture specific number of bytes for what is defined as a word */
            /* 4bytes in RISC-V's case */
            updates = 0;
            tempword = 0;
            while (updates < 4)
            {
                c = fgetc(source_file);
                DoByte(c, source_file, FALSE, TRUE);
            }
            UpdateShiftRegister('.', tempword);
            ip = ip - 4;
        }
        else if(in_set(c, "%&")) WordStorePointer(c, source_file);  /* Deal with % and & */
        else if(in_set(c, "!@$~"))
        {
            Clear_Scratch(scratch);
            consume_token(source_file);
            UpdateShiftRegister(c, Architectural_displacement(GetTarget(scratch), ip)); /* Play with shift register */
        }
        else if('<' == c) pad_to_align(TRUE);
        else if('^' == c) ALIGNED = TRUE;
        else DoByte(c, source_file, TRUE, FALSE);
    }

    fclose(source_file);
}

File /mescc-tools/hex2.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools/hex2.c'.
URL: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=hex2.c;id=08d5a0679fa4dc00babe02306f1bc50703083389
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
 * Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "hex2_globals.h"

/* The essential functions */
void first_pass(struct input_files* input);
void second_pass(struct input_files* input);
void WordFirstPass(struct input_files* input);
void WordSecondPass(struct input_files* input);

/* Standard C main program */
int main(int argc, char **argv)
{
    int InsaneArchitecture = FALSE;
    ALIGNED = FALSE;
    BigEndian = TRUE;
    jump_tables = calloc(65537, sizeof(struct entry*));
    require(NULL != jump_tables, "Failed to allocate our jump_tables\n");

    Architecture = KNIGHT;
    Base_Address = 0;
    struct input_files* input = NULL;
    output = stdout;
    char* output_file = "";
    exec_enable = TRUE;
    ByteMode = HEX;
    scratch = calloc(max_string + 1, sizeof(char));
    require(NULL != scratch, "failed to allocate our scratch buffer\n");
    char* arch;
    struct input_files* temp;

    int option_index = 1;
    while(option_index <= argc)
    {
        if(NULL == argv[option_index])
        {
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--big-endian"))
        {
            BigEndian = TRUE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--little-endian"))
        {
            BigEndian = FALSE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--non-executable"))
        {
            exec_enable = FALSE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-A") || match(argv[option_index], "--architecture"))
        {
            arch = argv[option_index + 1];
            if(match("knight-native", arch) || match("knight-posix", arch)) Architecture = KNIGHT;
            else if(match("x86", arch)) Architecture = X86;
            else if(match("amd64", arch)) Architecture = AMD64;
            else if(match("armv7l", arch)) Architecture = ARMV7L;
            else if(match("aarch64", arch)) Architecture = AARM64;
            else if(match("ppc64le", arch)) Architecture = PPC64LE;
            else if(match("riscv32", arch)) Architecture = RISCV32;
            else if(match("riscv64", arch)) Architecture = RISCV64;
            else
            {
                fputs("Unknown architecture: ", stderr);
                fputs(arch, stderr);
                fputs(" know values are: knight-native, knight-posix, x86, amd64, armv7l, riscv32 and riscv64", stderr);
            }
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-b") || match(argv[option_index], "--binary"))
        {
            ByteMode = BINARY;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-B") || match(argv[option_index], "--base-address"))
        {
            Base_Address = strtoint(argv[option_index + 1]);
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-h") || match(argv[option_index], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" --file FILENAME1 {-f FILENAME2} (--big-endian|--little-endian)", stderr);
            fputs(" [--base-address 0x12345] [--architecture name]\nArchitecture:", stderr);
            fputs(" knight-native, knight-posix, x86, amd64, armv7l, aarch64, riscv32 and riscv64\n", stderr);
            fputs("To leverage octal or binary input: --octal, --binary\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "-f") || match(argv[option_index], "--file"))
        {
            temp = calloc(1, sizeof(struct input_files));
            require(NULL != temp, "failed to allocate file for processing\n");
            temp->filename = argv[option_index + 1];
            temp->next = input;
            input = temp;
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-o") || match(argv[option_index], "--output"))
        {
            output_file = argv[option_index + 1];
            output = fopen(output_file, "w");

            if(NULL == output)
            {
                fputs("The file: ", stderr);
                fputs(argv[option_index + 1], stderr);
                fputs(" can not be opened!\n", stderr);
                exit(EXIT_FAILURE);
            }
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-O") || match(argv[option_index], "--octal"))
        {
            ByteMode = OCTAL;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-V") || match(argv[option_index], "--version"))
        {
            fputs("hex2 1.5.0\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    if((Architecture == RISCV32) || (Architecture == RISCV64))
    {
        /* Forcing me to use words instead of just byting into the problem */
        InsaneArchitecture = TRUE;
    }

    /* Catch a common mistake */
    if((KNIGHT != Architecture) && (0 == Base_Address))
    {
        fputs(">> WARNING <<\n>> WARNING <<\n>> WARNING <<\n", stderr);
        fputs("If you are not generating a ROM image this binary will likely not work\n", stderr);
    }

    /* Catch implicitly false assumptions */
    if(BigEndian && ((X86 == Architecture) || ( AMD64 == Architecture) || (ARMV7L == Architecture) || (AARM64 == Architecture) || (RISCV32 == Architecture) || (RISCV64 == Architecture)))
    {
        fputs(">> WARNING <<\n>> WARNING <<\n>> WARNING <<\n", stderr);
        fputs("You have specified big endian output on likely a little endian processor\n", stderr);
        fputs("if this is a mistake please pass --little-endian next time\n", stderr);
    }

    /* Make sure we have a program tape to run */
    if (NULL == input)
    {
        return EXIT_FAILURE;
    }

    /* Get all of the labels */
    ip = Base_Address;
    if(InsaneArchitecture) WordFirstPass(input);
    else first_pass(input);

    /* Fix all the references*/
    ip = Base_Address;
    if(InsaneArchitecture) WordSecondPass(input);
    else second_pass(input);

    /* flush all writes */
    fflush(output);

    /* Set file as executable */
    if(exec_enable && (output != stdout))
    {
        /* Close output file */
        fclose(output);

        if(0 != chmod(output_file, 0750))
        {
            fputs("Unable to change permissions\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    return EXIT_SUCCESS;
}

File /M2libc/x86/libc-full.M1

Live-bootstrap source file is 'seed/stage0-posix/M2libc/x86/libc-full.M1'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/x86/libc-full.M1
## Copyright (C) 2016 Jeremiah Orians
## This file is part of M2-Planet.
##
## M2-Planet is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## M2-Planet is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.

:_start

    mov_ebp,esp                 ; Protect esp

    ;; Prepare argv
    lea_eax,[ebp+DWORD] %4      ; ARGV_address = EBP + 4
    push_eax                    ; Put argv on the stack

    ;; Prepare envp
    mov_eax,ebp                 ; Address we need to load from
    mov_eax,[eax]               ; Get ARGC
    add_eax, %2                 ; OFFSET = ARGC + 2
    sal_eax, !2                 ; OFFSET = OFFSET * WORDSIZE
    add_eax,ebp                 ; ENVP_address = ESP + OFFSET
    push_eax                    ; Put envp on the stack
    mov_ebx, &GLOBAL__envp      ; Get _envp global
    mov_[ebx],eax               ; Save environment to _envp

    ;; Stack offset
    add_ebp, %4                 ; Fix ebp

    ;; Setup for malloc
    call %FUNCTION___init_malloc

    ;; Setup for FILE*
    call %FUNCTION___init_io

    ;; Perform the main loop
    call %FUNCTION_main
    push_eax                    ; Put return on stack
    push_eax                    ; so that _exit gets the value

:FUNCTION_exit
    call %FUNCTION___kill_io
:FUNCTION__exit
    pop_ebx
    pop_ebx
    mov_eax, %1
    int !0x80

:GLOBAL__envp
    NULL

File /M2libc/string.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/string.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/string.c
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stddef.h>

char* strcpy(char* dest, char const* src)
{
    int i = 0;

    while (0 != src[i])
    {
        dest[i] = src[i];
        i = i + 1;
    }
    dest[i] = 0;

    return dest;
}


char* strncpy(char* dest, char const* src, size_t count)
{
    if(0 == count) return dest;
    size_t i = 0;
    while(0 != src[i])
    {
        dest[i] = src[i];
        i = i + 1;
        if(count == i) return dest;
    }

    while(i <= count)
    {
        dest[i] = 0;
        i = i + 1;
    }

    return dest;
}


char* strcat(char* dest, char const* src)
{
    int i = 0;
    int j = 0;
    while(0 != dest[i]) i = i + 1;
    while(0 != src[j])
    {
        dest[i] = src[j];
        i = i + 1;
        j = j + 1;
    }
    dest[i] = 0;
    return dest;
}


char* strncat(char* dest, char const* src, size_t count)
{
    size_t i = 0;
    size_t j = 0;
    while(0 != dest[i]) i = i + 1;
    while(0 != src[j])
    {
        if(count == j)
        {
            dest[i] = 0;
            return dest;
        }
        dest[i] = src[j];
        i = i + 1;
        j = j + 1;
    }
    dest[i] = 0;
    return dest;
}


size_t strlen(char const* str )
{
    size_t i = 0;
    while(0 != str[i]) i = i + 1;
    return i;
}


size_t strnlen_s(char const* str, size_t strsz )
{
    size_t i = 0;
    while(0 != str[i])
    {
        if(strsz == i) return i;
        i = i + 1;
    }
    return i;
}


int strcmp(char const* lhs, char const* rhs )
{
    int i = 0;
    while(0 != lhs[i])
    {
        if(lhs[i] != rhs[i]) return lhs[i] - rhs[i];
        i = i + 1;
    }

    return lhs[i] - rhs[i];
}


int strncmp(char const* lhs, char const* rhs, size_t count)
{
    if(count == 0) return 0;

    size_t i = 0;
    while(0 != lhs[i])
    {
        if(lhs[i] != rhs[i]) return lhs[i] - rhs[i];
        i = i + 1;
        if(count <= i) return 0;
    }

    return lhs[i] - rhs[i];
}


char* strchr(char const* str, int ch)
{
    char* p = str;
    while(ch != p[0])
    {
        if(0 == p[0]) return NULL;
        p = p + 1;
    }
    if(0 == p[0]) return NULL;
    return p;
}


char* strrchr(char const* str, int ch)
{
    char* p = str;
    int i = 0;
    while(0 != p[i]) i = i + 1;
    while(ch != p[i])
    {
        if(0 == i) return NULL;
        i = i - 1;
    }
    return (p + i);
}


size_t strspn(char const* dest, char const* src)
{
    if(0 == dest[0]) return 0;
    int i = 0;
    while(NULL != strchr(src, dest[i])) i = i + 1;
    return i;
}


size_t strcspn(char const* dest, char const* src)
{
    int i = 0;
    while(NULL == strchr(src, dest[i])) i = i + 1;
    return i;
}


char* strpbrk(char const* dest, char const* breakset)
{
    char* p = dest;
    char* s;
    while(0 != p[0])
    {
        s = strchr(breakset, p[0]);
        if(NULL != s) return strchr(p,  s[0]);
        p = p + 1;
    }
    return p;
}


void* memset(void* dest, int ch, size_t count)
{
    if(NULL == dest) return dest;
    size_t i = 0;
    char* s = dest;
    while(i < count)
    {
        s[i] = ch;
        i = i + 1;
    }
    return dest;
}


void* memcpy(void* dest, void const* src, size_t count)
{
    if(NULL == dest) return dest;
    if(NULL == src) return NULL;

    char* s1 = dest;
    char const* s2 = src;
    size_t i = 0;
    while(i < count)
    {
        s1[i] = s2[i];
        i = i + 1;
    }
    return dest;
}

void* memmove(void* dest, void const* src, size_t count)
{
    if (dest < src) return memcpy (dest, src, count);
    char *p = dest;
    char const *q = src;
    count = count - 1;
    while (count >= 0)
    {
        p[count] = q[count];
        count = count - 1;
    }
    return dest;
}


int memcmp(void const* lhs, void const* rhs, size_t count)
{
    if(0 == count) return 0;
    size_t i = 0;
    count = count - 1;
    char const* s1 = lhs;
    char const* s2 = rhs;
    while(i < count)
    {
        if(s1[i] != s2[i]) break;
        i = i + 1;
    }
    return (s1[i] - s2[i]);
}

char* strstr(char* haystack, char* needle)
{
    int hl = strlen(haystack);
    int sl = strlen(needle);
    int i = 0;
    int max = hl - sl;
    if(hl < sl) return NULL;
    else if(hl == sl)
    {
        if(0 == strncmp(haystack, needle, hl)) return haystack;
        return NULL;
    }
    else
    {
        while(i <= max)
        {
            if(0 == strncmp(haystack+i, needle, hl)) return haystack+i;
            i = i + 1;
        }
        return NULL;
    }
}

File /mescc-tools/Kaem/kaem.h

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools/Kaem/kaem.h'.
URL: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=Kaem/kaem.h;id=08d5a0679fa4dc00babe02306f1bc50703083389
/* Copyright (C) 2016-2020 Jeremiah Orians
 * Copyright (C) 2020 fosslinux
 * This file is part of mescc-tools.
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include "../M2libc/bootstrappable.h"

/*
 * DEFINES
 */

#define FALSE 0
#define TRUE 1
// CONSTANT SUCCESS 0
#define SUCCESS 0
// CONSTANT FAILURE 1
#define FAILURE 1
#define MAX_STRING 4096
#define MAX_ARRAY 512


/*
 * Here is the token struct. It is used for both the token linked-list and
 * env linked-list.
 */
struct Token
{
    /*
     * For the token linked-list, this stores the token; for the env linked-list
     * this stores the value of the variable.
     */
    char* value;
    /*
     * Used only for the env linked-list. It holds a string containing the
     * name of the var.
     */
    char* var;
    /*
     * This struct stores a node of a singly linked list, store the pointer to
     * the next node.
     */
    struct Token* next;
};

#include "kaem_globals.h"

File /mescc-tools/Kaem/variable.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools/Kaem/variable.c'.
URL: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=Kaem/variable.c;id=08d5a0679fa4dc00babe02306f1bc50703083389
/*
 * Copyright (C) 2020 fosslinux
 * This file is part of mescc-tools.
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "kaem.h"

/* Prototypes from other files */
int array_length(char** array);
char* env_lookup(char* variable);

/*
 * VARIABLE HANDLING FUNCTIONS
 */

/* Substitute a variable into n->value */
int run_substitution(char* var_name, struct Token* n)
{
    char* value = env_lookup(var_name);
    /* If there is nothing to substitute, don't substitute anything! */
    if(value != NULL)
    {
        char* s = calloc(MAX_STRING, sizeof(char));
        s = strcat(s, n->value);
        s = strcat(s, value);
        n->value = s;
        return TRUE;
    }
    return FALSE;
}

/* Handle ${var:-text} format of variables - i.e. ifset format */
int variable_substitute_ifset(char* input, struct Token* n, int index)
{
    /*
     * In ${var:-text} format, we evaluate like follows.
     * If var is set as an envar, then we substitute the contents of that
     * envar. If it is not set, we substitute alternative text.
     *
     * In this function, we assume that input is the raw token,
     * n->value is everything already done in variable_substitute,
     * index is where we are up to in input. offset is for n->value.
     */

    /*
     * Check if we should even be performing this function.
     * We perform this function when we come across ${var:-text} syntax.
     */
    int index_old = index;
    int perform = FALSE;
    int input_length = strlen(input);
    while(index < input_length)
    { /* Loop over each character */
        if(input[index] == ':' && input[index + 1] == '-')
        { /* Yes, this is (most likely) ${var:-text} format. */
            perform = TRUE;
            break;
        }
        index = index + 1;
    }

    /* Don't perform it if we shouldn't */
    if(perform == FALSE) return index_old;
    index = index_old;

    /*
     * Get offset.
     * offset is the difference between the index of the variable we write to
     * in the following blocks and input.
     * This stays relatively constant.
     */
    int offset = index;

    /* Get the variable name */
    char* var_name = calloc(MAX_STRING, sizeof(char));
    require(var_name != NULL, "Memory initialization of var_name in variable_substitute_ifset failed\n");
    while(input[index] != ':')
    { /* Copy into var_name until :- */
        var_name[index - offset] = input[index];
        index = index + 1;
    }

    /* Skip over :- */
    index = index + 2;
    offset = index;

    /* Get the alternative text */
    char* text = calloc(MAX_STRING, sizeof(char));
    require(text != NULL, "Memory initialization of text in variable_substitute_ifset failed\n");
    while(input[index] != '}')
    { /* Copy into text until } */
        require(input_length > index, "IMPROPERLY TERMINATED VARIABLE\nABORTING HARD\n");
        text[index - offset] = input[index];
        index = index + 1;
    }

    /* Do the substitution */
    if(run_substitution(var_name, n) == FALSE)
    { /* The variable was not found. Substitute the alternative text. */
        char* s = calloc(MAX_STRING, sizeof(char));
        s = strcat(s, n->value);
        s = strcat(s, text);
        n->value = s;
    }

    return index;
}

/* Controls substitution for ${variable} and derivatives */
int variable_substitute(char* input, struct Token* n, int index)
{
    /* NOTE: index is the pos of input */
    index = index + 1; /* We don't want the { */

    /*
     * Check for "special" types
     * If we do find a special type we delegate the substitution to it
     * and return here; as we are done... there's nothing more do do in
     * that case.
     */
    int index_old = index;
    index = variable_substitute_ifset(input, n, index);
    if(index != index_old) return index;

    /* Reset index */
    index = index_old;

    /*
     * If we reach here it is a normal substitution
     * Let's do it!
     */
    /* Initialize var_name and offset */
    char* var_name = calloc(MAX_STRING, sizeof(char));
    require(var_name != NULL, "Memory initialization of var_name in variable_substitute failed\n");
    int offset = index;

    /* Get the variable name */
    int substitute_done = FALSE;
    char c;
    while(substitute_done == FALSE)
    {
        c = input[index];
        require(MAX_STRING > index, "LINE IS TOO LONG\nABORTING HARD\n");
        if(EOF == c || '\n' == c || index > strlen(input))
        { /* We never should hit EOF, EOL or run past the end of the line 
             while collecting a variable */
            fputs("IMPROPERLY TERMINATED VARIABLE!\nABORTING HARD\n", stderr);
            exit(EXIT_FAILURE);
        }
        else if('\\' == c)
        { /* Drop the \ - poor mans escaping. */
            index = index + 1;
        }
        else if('}' == c)
        { /* End of variable name */
            substitute_done = TRUE;
        }
        else
        {
            var_name[index - offset] = c;
            index = index + 1;
        }
    }

    /* Substitute the variable */
    run_substitution(var_name, n);

    return index;
}

/* Function to concatenate all command line arguments */
void variable_all(char** argv, struct Token* n)
{
    fflush(stdout);
    /* index refernences the index of n->value, unlike other functions */
    int index = 0;
    int argv_length = array_length(argv);
    int i = 0;
    char* argv_element = calloc(MAX_STRING, sizeof(char));
    char* hold = argv[i];
    n->value = argv_element;
    /* Assuming the form kaem -f script or kaem -f script -- 123 we want matching results to bash, so skip the kaem, -f and script */
    while(!match("--", hold))
    {
        i = i + 1;
        hold = argv[i];
        if(argv_length == i) break;
    }

    /* put i = i + 1 in the for initialization to skip past the -- */
    for(; i < argv_length; i = i + 1)
    {
        /* Ends up with (n->value) (argv[i]) */
        /* If we don't do this we get jumbled results in M2-Planet */
        hold = argv[i];
        strcpy(argv_element + index, hold);
        index = index + strlen(hold);

        /* Add space on the end */
        n->value[index] = ' ';
        index = index + 1;
    }
    /* Remove trailing space */
    index = index - 1;
    n->value[index] = 0;
}

/* Function controlling substitution of variables */
void handle_variables(char** argv, struct Token* n)
{
    /* NOTE: index is the position of input */
    int index = 0;

    /* Create input */
    char* input = calloc(MAX_STRING, sizeof(char));
    require(input != NULL, "Memory initialization of input in collect_variable failed\n");
    strcpy(input, n->value);
    /* Reset n->value */
    n->value = calloc(MAX_STRING, sizeof(char));
    require(n->value != NULL, "Memory initialization of n->value in collect_variable failed\n");

    /* Copy everything up to the $ */
    /*
     * TODO: Not need allocation of input before this check if there is no
     * variable in it.
     */
    while(input[index] != '$')
    {
        if(input[index] == 0)
        { /* No variable in it */
            n->value = input;
            return; /* We don't need to do anything more */
        }
        n->value[index] = input[index];
        index = index + 1;
    }

    /* Must be outside the loop */
    int offset;

substitute:
    index = index + 1; /* We are uninterested in the $ */
    /* Run the substitution */
    if(input[index] == '{')
    { /* Handle everything ${ related */
        index = variable_substitute(input, n, index);
        index = index + 1; /* We don't want the closing } */
    }
    else if(input[index] == '@')
    { /* Handles $@ */
        index = index + 1; /* We don't want the @ */
        variable_all(argv, n);
    }
    else
    { /* We don't know that */
        fputs("IMPROPERLY USED VARIABLE!\nOnly ${foo} and $@ format are accepted at this time.\nABORTING HARD\n", stderr);
        exit(EXIT_FAILURE);
    }

    offset = strlen(n->value) - index;
    /* Copy everything from the end of the variable to the end of the token */
    while(input[index] != 0)
    {
        if(input[index] == '$')
        { /* We have found another variable */
            fflush(stdout);
            goto substitute;
        }
        n->value[index + offset] = input[index];
        index = index + 1;
    }
}

File /mescc-tools/Kaem/kaem_globals.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools/Kaem/kaem_globals.c'.
URL: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=Kaem/kaem_globals.c;id=08d5a0679fa4dc00babe02306f1bc50703083389
/* Copyright (C) 2016-2020 Jeremiah Orians
 * Copyright (C) 2020 fosslinux
 * This file is part of mescc-tools.
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "kaem.h"

int command_done;
int VERBOSE;
int VERBOSE_EXIT;
int STRICT;
int INIT_MODE;
int FUZZING;
int WARNINGS;
char* KAEM_BINARY;
char* PATH;

/* Token linked-list; stores the tokens of each line */
struct Token* token;
/* Env linked-list; stores the environment variables */
struct Token* env;
/* Alias linked-list; stores the aliases */
struct Token* alias;

File /mescc-tools/Kaem/kaem.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools/Kaem/kaem.c'.
URL: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=Kaem/kaem.c;id=08d5a0679fa4dc00babe02306f1bc50703083389
/* Copyright (C) 2016-2020 Jeremiah Orians
 * Copyright (C) 2020 fosslinux
 * Copyright (C) 2021 Andrius Å tikonas
 * This file is part of mescc-tools.
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include "kaem.h"

/* Prototypes from other files */
void handle_variables(char** argv, struct Token* n);

/*
 * UTILITY FUNCTIONS
 */

/* Function to find a character in a string */
char* find_char(char* string, char a)
{
    if(0 == string[0])
    {
        return NULL;
    }

    while(a != string[0])
    {
        string = string + 1;

        if(0 == string[0])
        {
            return string;
        }
    }

    return string;
}

/* Function to find the length of a char**; an array of strings */
int array_length(char** array)
{
    int length = 0;

    while(array[length] != NULL)
    {
        length = length + 1;
    }

    return length;
}

/* Search for a variable in the token linked-list */
char* token_lookup(char* variable, struct Token* token)
{
    /* Start at the head */
    struct Token* n = token;

    /* Loop over the linked-list */
    while(n != NULL)
    {
        if(match(variable, n->var))
        {
            /* We have found the correct node */
            return n->value; /* Done */
        }

        /* Nope, try the next */
        n = n->next;
    }

    /* We didn't find anything! */
    return NULL;
}

/* Search for a variable in the env linked-list */
char* env_lookup(char* variable)
{
    return token_lookup(variable, env);
}

/* Search for a variable in the alias linked-list */
char* alias_lookup(char* variable)
{
    return token_lookup(variable, alias);
}

/* Find the full path to an executable */
char* find_executable(char* name)
{
    if(match("", name))
    {
        return NULL;
    }

    if(('.' == name[0]) || ('/' == name[0]))
    {
        /* assume names that start with . or / are relative or absolute */
        return name;
    }

    char* trial = calloc(MAX_STRING, sizeof(char));
    char* MPATH = calloc(MAX_STRING, sizeof(char)); /* Modified PATH */
    require(MPATH != NULL, "Memory initialization of MPATH in find_executable failed\n");
    strcpy(MPATH, PATH);
    FILE* t;
    char* next = find_char(MPATH, ':');
    int index;
    int offset;
    int mpath_length;
    int name_length;
    int trial_length;

    while(NULL != next)
    {
        /* Reset trial */
        trial_length = strlen(trial);

        for(index = 0; index < trial_length; index = index + 1)
        {
            trial[index] = 0;
        }

        next[0] = 0;
        /* prepend_string(MPATH, prepend_string("/", name)) */
        mpath_length = strlen(MPATH);

        for(index = 0; index < mpath_length; index = index + 1)
        {
            require(MAX_STRING > index, "Element of PATH is too long\n");
            trial[index] = MPATH[index];
        }

        trial[index] = '/';
        offset = strlen(trial);
        name_length = strlen(name);

        for(index = 0; index < name_length; index = index + 1)
        {
            require(MAX_STRING > index, "Element of PATH is too long\n");
            trial[index + offset] = name[index];
        }

        /* Try the trial */
        require(strlen(trial) < MAX_STRING, "COMMAND TOO LONG!\nABORTING HARD\n");
        t = fopen(trial, "r");

        if(NULL != t)
        {
            fclose(t);
            return trial;
        }

        MPATH = next + 1;
        next = find_char(MPATH, ':');
    }

    return NULL;
}

/* Function to convert a Token linked-list into an array of strings */
char** list_to_array(struct Token* s)
{
    struct Token* n;
    n = s;
    char** array = calloc(MAX_ARRAY, sizeof(char*));
    require(array != NULL, "Memory initialization of array in conversion of list to array failed\n");
    char* element = calloc(MAX_STRING, sizeof(char));
    require(element != NULL, "Memory initialization of element in conversion of list to array failed\n");
    int index = 0;
    int i;
    int value_length;
    int var_length;
    int offset;

    while(n != NULL)
    {
        /* Loop through each node and assign it to an array index */
        array[index] = calloc(MAX_STRING, sizeof(char));
        require(array[index] != NULL, "Memory initialization of array[index] in conversion of list to array failed\n");
        /* Bounds checking */
        /* No easy way to tell which it is, output generic message */
        require(index < MAX_ARRAY, "SCRIPT TOO LONG or TOO MANY ENVARS\nABORTING HARD\n");

        if(n->var == NULL)
        {
            /* It is a line */
            array[index] = n->value;
        }
        else
        {
            /* It is a var */
            /* prepend_string(n->var, prepend_string("=", n->value)) */
            var_length = strlen(n->var);

            for(i = 0; i < var_length; i = i + 1)
            {
                element[i] = n->var[i];
            }

            element[i] = '=';
            i = i + 1;
            offset = i;
            value_length = strlen(n->value);

            for(i = 0; i < value_length; i = i + 1)
            {
                element[i + offset] = n->value[i];
            }
        }

        /* Insert elements if not empty */
        if(!match("", element))
        {
            strcpy(array[index], element);
        }

        n = n->next;
        index = index + 1;

        /* Reset element */
        for(i = 0; i < MAX_STRING; i = i + 1)
        {
            element[i] = 0;
        }
    }

    return array;
}

/* Function to handle the correct options for escapes */
int handle_escape(int c)
{
    if(c == '\n')
    {
        /* Do nothing - eat up the newline */
        return -1;
    }
    else if('n' == c)
    {
        /* Add a newline to the token */
        return '\n';
    }
    else if('r' == c)
    {
        /* Add a return to the token */
        return '\r';
    }
    else if('\\' == c)
    {
        /* Add a real backslash to the token */
        return '\\';
    }
    else
    {
        /* Just add it to the token (eg, quotes) */
        return c;
    }
}

/*
 * TOKEN COLLECTION FUNCTIONS
 */

/* Function for skipping over line comments */
void collect_comment(FILE* input)
{
    int c;

    /* Eat up the comment, one character at a time */
    /*
     * Sanity check that the comment ends with \n.
     * Remove the comment from the FILE*
     */
    do
    {
        c = fgetc(input);
        /* We reached an EOF!! */
        require(EOF != c, "IMPROPERLY TERMINATED LINE COMMENT!\nABORTING HARD\n");
    } while('\n' != c); /* We can now be sure it ended with \n -- and have purged the comment */
}

/* Function for collecting strings and removing the "" pair that goes with them */
int collect_string(FILE* input, char* n, int index)
{
    int string_done = FALSE;
    int c;

    do
    {
        /* Bounds check */
        require(MAX_STRING > index, "LINE IS TOO LONG\nABORTING HARD\n");
        c = fgetc(input);
        require(EOF != c, "IMPROPERLY TERMINATED STRING!\nABORTING HARD\n");

        if('\\' == c)
        {
            /* We are escaping the next character */
            /* This correctly handles escaped quotes as it just returns the quote */
            c = fgetc(input);
            c = handle_escape(c);
            n[index] = c;
            index = index + 1;
        }
        else if('"' == c)
        {
            /* End of string */
            string_done = TRUE;
        }
        else
        {
            n[index] = c;
            index = index + 1;
        }
    } while(string_done == FALSE);

    return index;
}

/* Function to parse and assign token->value */
int collect_token(FILE* input, char* n, int last_index)
{
    int c;
    int cc;
    int token_done = FALSE;
    int index = 0;

    do
    {
        /* Loop over each character in the token */
        c = fgetc(input);
        /* Bounds checking */
        require(MAX_STRING > index, "LINE IS TOO LONG\nABORTING HARD\n");

        if(EOF == c)
        {
            /* End of file -- this means script complete */
            /* We don't actually exit here. This logically makes more sense;
             * let the code follow its natural path of execution and exit
             * sucessfuly at the end of main().
             */
            token_done = TRUE;
            command_done = TRUE;
            return -1;
        }
        else if((' ' == c) || ('\t' == c))
        {
            /* Space and tab are token separators */
            token_done = TRUE;
        }
        else if(('\n' == c) || (';' == c))
        {
            /* Command terminates at the end of a line or at semicolon */
            command_done = TRUE;
            token_done = TRUE;

            if(0 == index)
            {
                index = last_index;
            }
        }
        else if('"' == c)
        {
            /* Handle strings -- everything between a pair of "" */
            index = collect_string(input, n, index);
            token_done = TRUE;
        }
        else if('#' == c)
        {
            /* Handle line comments */
            collect_comment(input);
            command_done = TRUE;
            token_done = TRUE;

            if(0 == index)
            {
                index = last_index;
            }
        }
        else if('\\' == c)
        {
            /* Support for escapes */
            c = fgetc(input); /* Skips over \, gets the next char */
            cc = handle_escape(c);

            if(-1 != cc)
            {
                /* We need to put it into the token */
                n[index] = cc;
            }

            index = index + 1;
        }
        else if(0 == c)
        {
            /* We have come to the end of the token */
            token_done = TRUE;
        }
        else
        {
            /* It's a character to assign */
            n[index] = c;
            index = index + 1;
        }
    } while(token_done == FALSE);

    return index;
}

/* Function to parse string and assign token->value */
int collect_alias_token(char* input, char* n, int index)
{
    int c;
    int cc;
    int token_done = FALSE;
    int output_index = 0;

    do
    {
        /* Loop over each character in the token */
        c = input[index];
        index = index + 1;

        if((' ' == c) || ('\t' == c))
        {
            /* Space and tab are token separators */
            token_done = TRUE;
        }
        else if('\\' == c)
        {
            /* Support for escapes */
            c = input[index];
            index = index + 1;
            cc = handle_escape(c);

            /* We need to put it into the token */
            n[output_index] = cc;
            output_index = output_index + 1;
        }
        else if(0 == c)
        {
            /* We have come to the end of the token */
            token_done = TRUE;
            index = 0;
        }
        else
        {
            /* It's a character to assign */
            n[output_index] = c;
            output_index = output_index + 1;
        }
    } while(token_done == FALSE);

        /* Terminate the output with a NULL */
        n[output_index] = 0;

    return index;
}

/*
 * EXECUTION FUNCTIONS
 * Note: All of the builtins return SUCCESS (0) when they exit successfully
 * and FAILURE (1) when they fail.
 */

/* Function to check if the token is an envar */
int is_envar(char* token)
{
    int i = 0;
    int token_length = strlen(token);

    while(i < token_length)
    {
        if(token[i] == '=')
        {
            return FAILURE;
        }

        i = i + 1;
    }

    return SUCCESS;
}

/* Add an envar */
void add_envar()
{
    /* Pointers to strings we want */
    char* name = calloc(strlen(token->value) + 4, sizeof(char));
    char* value = token->value;
    char* newvalue;
    int i = 0;

    /* Isolate the name */
    while('=' != value[i])
    {
        name[i] = value[i];
        i = i + 1;
    }

    /* Isolate the value */
    newvalue = name + i + 2;
    value = value + i + 1;
    i = 0;
    require(0 != value[i], "add_envar received improper variable\n");

    while(0 != value[i])
    {
        newvalue[i] = value[i];
        i = i + 1;
    }

    /* If we are in init-mode and this is the first var env == NULL, rectify */
    if(env == NULL)
    {
        env = calloc(1, sizeof(struct Token));
        require(env != NULL, "Memory initialization of env failed\n");
        env->var = name; /* Add our first variable */
    }

    /*
     * If the name of the envar is PATH, then we need to set our (internal)
     * global PATH value.
     */
    if(match(name, "PATH"))
    {
        strcpy(PATH, newvalue);
    }

    struct Token* n = env;

    /* Find match if possible */
    while(!match(name, n->var))
    {
        if(NULL == n->next)
        {
            n->next = calloc(1, sizeof(struct Token));
            require(n->next != NULL, "Memory initialization of next env node in add_envar failed\n");
            n->next->var = name;
        } /* Loop will match and exit */

        n = n->next;
    }

    /* Since we found the variable we need only to set it to its new value */
    n->value = newvalue;
}

/* Add an alias */
void add_alias()
{
    token = token->next; /* Skip the actual alias */
    if(token->next == NULL)
    {
        /* No arguments */
        char** array = list_to_array(alias);
        int index = 0;
        while(array[index] != NULL) {
            fputs(array[index], stdout);
            fputc('\n', stdout);
            index = index + 1;
        }
        fflush(stdout);
        return;
    }
    if(!is_envar(token->value)) {
        char** array = list_to_array(token);
        int index = 0;
        while(array[index] != NULL) {
            fputs(array[index], stdout);
            fputc(' ', stdout);
            index = index + 1;
        }
        fputc('\n', stdout);
        fflush(stdout);
        return;
    }

    /* Pointers to strings we want */
    char* name = calloc(strlen(token->value) + 4, sizeof(char));
    char* value = token->value;
    char* newvalue;
    int i = 0;

    /* Isolate the name */
    while('=' != value[i])
    {
        name[i] = value[i];
        i = i + 1;
    }

    /* Isolate the value */
    newvalue = name + i + 2;
    value = value + i + 1;
    i = 0;
    require(0 != value[i], "add_alias received improper variable\n");

    while(0 != value[i])
    {
        newvalue[i] = value[i];
        i = i + 1;
    }

    /* If this is the first alias, rectify */
    if(alias == NULL)
    {
        alias = calloc(1, sizeof(struct Token));
        require(alias != NULL, "Memory initialization of alias failed\n");
        alias->var = name; /* Add our first variable */
    }

    struct Token* n = alias;

    /* Find match if possible */
    while(!match(name, n->var))
    {
        if(NULL == n->next)
        {
            n->next = calloc(1, sizeof(struct Token));
            require(n->next != NULL, "Memory initialization of next alias node in alias failed\n");
            n->next->var = name;
        } /* Loop will match and exit */

        n = n->next;
    }

    /* Since we found the variable we need only to set it to its new value */
    n->value = newvalue;
}

/* cd builtin */
int cd()
{
    if(NULL == token->next)
    {
        return FAILURE;
    }

    token = token->next;

    if(NULL == token->value)
    {
        return FAILURE;
    }

    int ret = chdir(token->value);

    if(0 > ret)
    {
        return FAILURE;
    }

    return SUCCESS;
}

/* pwd builtin */
int pwd()
{
    char* path = calloc(MAX_STRING, sizeof(char));
    require(path != NULL, "Memory initialization of path in pwd failed\n");
    getcwd(path, MAX_STRING);
    require(!match("", path), "getcwd() failed\n");
    fputs(path, stdout);
    fputs("\n", stdout);
    return SUCCESS;
}

/* set builtin */
int set()
{
    /* Get the options */
    int i;

    if(NULL == token->next)
    {
        goto cleanup_set;
    }

    token = token->next;

    if(NULL == token->value)
    {
        goto cleanup_set;
    }

    char* options = calloc(MAX_STRING, sizeof(char));
    require(options != NULL, "Memory initialization of options in set failed\n");
    int last_position = strlen(token->value) - 1;

    for(i = 0; i < last_position; i = i + 1)
    {
        options[i] = token->value[i + 1];
    }

    /* Parse the options */
    int options_length = strlen(options);

    for(i = 0; i < options_length; i = i + 1)
    {
        if(options[i] == 'a')
        {
            /* set -a is on by default and cannot be disabled at this time */
            if(WARNINGS)
            {
                fputs("set -a is on by default and cannot be disabled\n", stdout);
            }

            continue;
        }
        else if(options[i] == 'e')
        {
            /* Fail on failure */
            STRICT = TRUE;
        }
        else if(options[i] == 'x')
        {
            /* Show commands as executed */
            /* TODO: this currently behaves like -v. Make it do what it should */
            VERBOSE = TRUE;
            /*
             * Output the set -x because VERBOSE didn't catch it before.
             * We don't do just -x because we support multiple options in one command,
             * eg set -ex.
             */
            fputs(" +> set -", stdout);
            fputs(options, stdout);
            fputs("\n", stdout);
            fflush(stdout);
        }
        else
        {
            /* Invalid */
            fputc(options[i], stderr);
            fputs(" is an invalid set option!\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    return SUCCESS;
cleanup_set:
    return FAILURE;
}

/* echo builtin */
void echo()
{
    if(token->next == NULL)
    {
        /* No arguments */
        fputs("\n", stdout);
        return;
    }

    if(token->next->value == NULL)
    {
        /* No arguments */
        fputs("\n", stdout);
        return;
    }

    token = token->next; /* Skip the actual echo */

    while(token != NULL)
    {
        /* Output each argument to echo to stdout */
        if(token->value == NULL)
        {
            break;
        }

        fputs(token->value, stdout);
        if(NULL != token->next)
        {
            /* M2-Planet doesn't short circuit */
            if(NULL != token->next->value) fputc(' ', stdout);
        }
        token = token->next;
    }

    fputs("\n", stdout);
}

/* unset builtin */
void unset()
{
    struct Token* e;
    /* We support multiple variables on the same line */
    struct Token* t;

    for(t = token->next; t != NULL; t = t->next)
    {
        if(NULL == t->value)
        {
            continue;
        }

        e = env;

        /* Look for the variable; we operate on ->next because we need to remove ->next */
        while(e->next != NULL)
        {
            if(match(e->next->var, t->value))
            {
                break;
            }

            e = e->next;
        }

        if(e->next != NULL)
        {
            /* There is something to unset */
            e->next = e->next->next;
        }

    }
}

void execute(FILE* script, char** argv);
int _execute(FILE* script, char** argv);
int collect_command(FILE* script, char** argv);

/* if builtin */
void if_cmd(FILE* script, char** argv)
{
    int index;
    int old_VERBOSE;
    token = token->next; /* Skip the actual if */
    /* Do not check for successful exit status */
    int if_status = _execute(script, argv);
    old_VERBOSE = VERBOSE;
    VERBOSE = VERBOSE && !if_status;

    do
    {
        index = collect_command(script, argv);
        require(index != -1, "Unexpected EOF, improperly terminated if statement.\n");

        if(0 == index)
        {
            continue;
        }

        if(0 == if_status)
        {
            /* Stuff to exec */
            execute(script, argv);
        }

        if(match(token->value, "else"))
        {
            if_status = !if_status;
        }
    } while(!match(token->value, "fi"));

    VERBOSE = old_VERBOSE;
}

int what_exit(char* program, int status)
{
    /***********************************************************************************
     * If the low-order 8 bits of w_status are equal to 0x7F or zero, the child        *
     * process has stopped. If the low-order 8 bits of w_status are non-zero and are   *
     * not equal to 0x7F, the child process terminated due to a signal otherwise, the  *
     * child process terminated due to an exit() call.                                 *
     *                                                                                 *
     * In the event it was a signal that stopped the process the top 8 bits of         *
     * w_status contain the signal that caused the process to stop.                    *
     *                                                                                 *
     * In the event it was terminated the bottom 7 bits of w_status contain the        *
     * terminating error number for the process.                                       *
     *                                                                                 *
     * If bit 0x80 of w_status is set, a core dump was produced.                       *
     ***********************************************************************************/

    int WIFEXITED = !(status & 0x7F);
    int WEXITSTATUS = (status & 0xFF00) >> 8;
    int WTERMSIG = status & 0x7F;
    int WCOREDUMP = status & 0x80;
    int WIFSIGNALED = !((0x7F == WTERMSIG) || (0 == WTERMSIG));
    int WIFSTOPPED = ((0x7F == WTERMSIG) && (0 == WCOREDUMP));

    if(WIFEXITED)
    {
        if(VERBOSE_EXIT)
        {
            fputc('\n', stderr);
            fputs(program, stderr);
            fputs(" normal termination, exit status = ", stderr);
            fputs(int2str(WEXITSTATUS, 10, TRUE), stderr);
            fputs("\n\n\n", stderr);
        }
        return WEXITSTATUS;
    }
    else if (WIFSIGNALED)
    {
        fputc('\n', stderr);
        fputs(program, stderr);
        fputs(" abnormal termination, signal number = ", stderr);
        fputs(int2str(WTERMSIG, 10, TRUE), stderr);
        fputc('\n', stderr);
        if(WCOREDUMP) fputs("core dumped\n", stderr);
        return WTERMSIG;
    }
    else if(WIFSTOPPED)
    {
        fputc('\n', stderr);
        fputs(program, stderr);
        fputs(" child stopped, signal number = ", stderr);
        fputs(int2str(WEXITSTATUS, 10, TRUE), stderr);
        fputc('\n', stderr);
        return WEXITSTATUS;
    }

    fputc('\n', stderr);
    fputs(program, stderr);
    fputs(" :: something crazy happened with execve\nI'm just gonna get the hell out of here\n", stderr);
    exit(EXIT_FAILURE);
}

/* Execute program and check for error */
void execute(FILE* script, char** argv)
{
    int status = _execute(script, argv);

    if(STRICT == TRUE && (0 != status))
    {
        /* Clearly the script hit an issue that should never have happened */
        fputs("Subprocess error ", stderr);
        fputs(int2str(status, 10, TRUE), stderr);
        fputs("\nABORTING HARD\n", stderr);
        exit(EXIT_FAILURE);
    }
}

/* Execute program */
int _execute(FILE* script, char** argv)
{
    /* Run the command */
    /* rc = return code */
    int rc;
    /* exec without forking */
    int exec = FALSE;

    /* Actually do the execution */
    if(is_envar(token->value) == TRUE)
    {
        add_envar();
        return 0;
    }
    else if(match(token->value, "cd"))
    {
        rc = cd();

        if(STRICT)
        {
            require(rc == SUCCESS, "cd failed!\n");
        }

        return 0;
    }
    else if(match(token->value, "set"))
    {
        rc = set();

        if(STRICT)
        {
            require(rc == SUCCESS, "set failed!\n");
        }

        return 0;
    }
    else if(match(token->value, "alias"))
    {
        add_alias();
        return 0;
    }
    else if(match(token->value, "pwd"))
    {
        rc = pwd();

        if(STRICT)
        {
            require(rc == SUCCESS, "pwd failed!\n");
        }

        return 0;
    }
    else if(match(token->value, "echo"))
    {
        echo();
        return 0;
    }
    else if(match(token->value, "unset"))
    {
        unset();
        return 0;
    }
    else if(match(token->value, "exec"))
    {
        token = token->next; /* Skip the actual exec */
        exec = TRUE;
    }
    else if(match(token->value, "if"))
    {
        if_cmd(script, argv);
        return 0;
    }
    else if(match(token->value, "then"))
    {
        /* ignore */
        return 0;
    }
    else if(match(token->value, "else"))
    {
        /* ignore */
        return 0;
    }
    else if(match(token->value, "fi"))
    {
        /* ignore */
        return 0;
    }

    /* If it is not a builtin, run it as an executable */
    int status; /* i.e. return code */
    char** array;
    char** envp;
    /* Get the full path to the executable */
    char* program = find_executable(token->value);

    /* Check we can find the executable */
    if(NULL == program)
    {
        if(STRICT == TRUE)
        {
            fputs("WHILE EXECUTING ", stderr);
            fputs(token->value, stderr);
            fputs(" NOT FOUND!\nABORTING HARD\n", stderr);
            exit(EXIT_FAILURE);
        }

        /* If we are not strict simply return */
        return 0;
    }

    int f = 0;

#ifdef __uefi__
    array = list_to_array(token);
    envp = list_to_array(env);
    return spawn(program, array, envp);
#else
    if(!exec)
    {
        f = fork();
    }

    /* Ensure fork succeeded */
    if(f == -1)
    {
        fputs("WHILE EXECUTING ", stderr);
        fputs(token->value, stderr);
        fputs(" fork() FAILED\nABORTING HARD\n", stderr);
        exit(EXIT_FAILURE);
    }
    else if(f == 0)
    {
        /* Child */
        /**************************************************************
         * Fuzzing produces random stuff; we don't want it running    *
         * dangerous commands. So we just don't execve.               *
         * But, we still do the list_to_array calls to check for      *
         * segfaults.                                                 *
         **************************************************************/
        array = list_to_array(token);
        envp = list_to_array(env);

        if(FALSE == FUZZING)
        {
            /* We are not fuzzing */
            /* execve() returns only on error */
            execve(program, array, envp);
        }

        /* Prevent infinite loops */
        _exit(EXIT_FAILURE);
    }

    /* Otherwise we are the parent */
    /* And we should wait for it to complete */
    waitpid(f, &status, 0);
    return what_exit(program, status);
#endif
}

int collect_command(FILE* script, char** argv)
{
    command_done = FALSE;
    /* Initialize token */
    struct Token* n;
    n = calloc(1, sizeof(struct Token));
    require(n != NULL, "Memory initialization of token in collect_command failed\n");
    char* s = calloc(MAX_STRING, sizeof(char));
    require(s != NULL, "Memory initialization of token in collect_command failed\n");
    token = n;
    int index = 0;
    int alias_index;
    char* alias_string;

    /* Get the tokens */
    while(command_done == FALSE)
    {
        index = collect_token(script, s, index);
        /* Don't allocate another node if the current one yielded nothing, OR
         * if we are done.
         */

        if(match(s, ""))
        {
            continue;
        }

        alias_string = alias_lookup(s);
        alias_index = 0;
        do
        {
            if(alias_string != NULL)
            {
                alias_index = collect_alias_token(alias_string, s, alias_index);
            }

            /* add to token */
            n->value = s;
            s = calloc(MAX_STRING, sizeof(char));
            require(s != NULL, "Memory initialization of next token node in collect_command failed\n");
            /* Deal with variables */
            handle_variables(argv, n);

            /* If the variable expands into nothing */
            if(match(n->value, " "))
            {
                n->value = NULL;
                continue;
            }

            /* Prepare for next loop */
            n->next = calloc(1, sizeof(struct Token));
            require(n->next != NULL, "Memory initialization of next token node in collect_command failed\n");
            n = n->next;
        }
        while(alias_index != 0);
    }

    /* -1 means the script is done */
    if(EOF == index)
    {
        return index;
    }

    /* Output the command if verbose is set */
    /* Also if there is nothing in the command skip over */
    if(VERBOSE && !match(token->value, "") && !match(token->value, NULL))
    {
        n = token;
        fputs(" +>", stdout);

        while(n != NULL)
        {
            /* Print out each token token */
            fputs(" ", stdout);

            /* M2-Planet doesn't let us do this in the while */
            if(n->value != NULL)
            {
                if(!match(n->value, ""))
                {
                    fputs(n->value, stdout);
                }
            }

            n = n->next;
        }

        fputc('\n', stdout);
        fflush(stdout);
    }

    return index;
}

/* Function for executing our programs with desired arguments */
void run_script(FILE* script, char** argv)
{
    int index;

    while(TRUE)
    {
        /*
         * Tokens has to be reset each time, as we need a new linked-list for
         * each line.
         * See, the program flows like this as a high level overview:
         * Get line -> Sanitize line and perform variable replacement etc ->
         * Execute line -> Next.
         * We don't need the previous lines once they are done with, so tokens
         * are hence for each line.
         */
        index = collect_command(script, argv);

        /* -1 means the script is done */
        if(EOF == index)
        {
            break;
        }

        if(0 == index)
        {
            continue;
        }

        /* Stuff to exec */
        execute(script, argv);
    }
}

/* Function to populate env */
void populate_env(char** envp)
{
    /* You can't populate a NULL environment */
    if(NULL == envp)
    {
        return;
    }

    /* avoid empty arrays */
    int max = array_length(envp);

    if(0 == max)
    {
        return;
    }

    /* Initialize env and n */
    env = calloc(1, sizeof(struct Token));
    require(env != NULL, "Memory initialization of env failed\n");
    struct Token* n;
    n = env;
    int i;
    int j;
    int k;
    char* envp_line;

    for(i = 0; i < max; i = i + 1)
    {
        n->var = calloc(MAX_STRING, sizeof(char));
        require(n->var != NULL, "Memory initialization of n->var in population of env failed\n");
        n->value = calloc(MAX_STRING, sizeof(char));
        require(n->value != NULL, "Memory initialization of n->var in population of env failed\n");
        j = 0;
        /*
         * envp is weird.
         * When referencing envp[i]'s characters directly, they were all jumbled.
         * So just copy envp[i] to envp_line, and work with that - that seems
         * to fix it.
         */
        envp_line = calloc(MAX_STRING, sizeof(char));
        require(envp_line != NULL, "Memory initialization of envp_line in population of env failed\n");
        require(strlen(envp[i]) < MAX_STRING, "Environment variable exceeds length restriction\n");
        strcpy(envp_line, envp[i]);

        while(envp_line[j] != '=')
        {
            /* Copy over everything up to = to var */
            n->var[j] = envp_line[j];
            j = j + 1;
        }

        /* If we get strange input, we need to ignore it */
        if(n->var == NULL)
        {
            continue;
        }

        j = j + 1; /* Skip over = */
        k = 0; /* As envp[i] will continue as j but n->value begins at 0 */

        while(envp_line[j] != 0)
        {
            /* Copy everything else to value */
            n->value[k] = envp_line[j];
            j = j + 1;
            k = k + 1;
        }

        /* Sometimes, we get lines like VAR=, indicating nothing is in the variable */
        if(n->value == NULL)
        {
            n->value = "";
        }

        /* Advance to next part of linked list */
        n->next = calloc(1, sizeof(struct Token));
        require(n->next != NULL, "Memory initialization of n->next in population of env failed\n");
        n = n->next;
    }

    /* Get rid of node on the end */
    n = NULL;
    /* Also destroy the n->next reference */
    n = env;

    while(n->next->var != NULL)
    {
        n = n->next;
    }

    n->next = NULL;
}

int main(int argc, char** argv, char** envp)
{
    VERBOSE = FALSE;
    VERBOSE_EXIT = FALSE;
    STRICT = TRUE;
    FUZZING = FALSE;
    WARNINGS = FALSE;
    char* filename = "kaem.run";
    FILE* script = NULL;
    /* Initalize structs */
    token = calloc(1, sizeof(struct Token));
    require(token != NULL, "Memory initialization of token failed\n");
    if(NULL != argv[0]) KAEM_BINARY = argv[0];
    else KAEM_BINARY = "./bin/kaem";
    int i = 1;

    /* Loop over arguments */
    while(i <= argc)
    {
        if(NULL == argv[i])
        {
            /* Ignore the argument */
            i = i + 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            /* Help information */
            fputs("Usage: ", stdout);
            fputs(argv[0], stdout);
            fputs(" [-h | --help] [-V | --version] [--file filename | -f filename] [-i | --init-mode] [-v | --verbose] [--non-strict] [--warn] [--fuzz]\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            /* Set the filename */
            if(argv[i + 1] != NULL)
            {
                filename = argv[i + 1];
            }

            i = i + 2;
        }
        else if(match(argv[i], "-i") || match(argv[i], "--init-mode"))
        {
            /* init mode does not populate env */
            INIT_MODE = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "-V") || match(argv[i], "--version"))
        {
            /* Output version */
            fputs("kaem version 1.5.0\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-v") || match(argv[i], "--verbose"))
        {
            /* Set verbose */
            VERBOSE = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "--strict"))
        {
            /* it is a NOP */
            STRICT = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "--non-strict"))
        {
            /* Set strict */
            STRICT = FALSE;
            i = i + 1;
        }
        else if(match(argv[i], "--warn"))
        {
            /* Set warnings */
            WARNINGS = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "--fuzz"))
        {
            /* Set fuzzing */
            FUZZING = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "--show-exit-codes"))
        {
            /* show exit codes */
            VERBOSE_EXIT = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "--"))
        {
            /* Nothing more after this */
            break;
        }
        else
        {
            /* We don't know this argument */
            fputs("UNKNOWN ARGUMENT\n", stdout);
            exit(EXIT_FAILURE);
        }
    }

    /* Populate env */
    if(INIT_MODE == FALSE)
    {
        populate_env(envp);
    }

    /* make sure SHELL is set */
    if(NULL == env_lookup("SHELL"))
    {
        struct Token* shell = calloc(1, sizeof(struct Token));
        require(NULL != shell, "unable to create SHELL environment variable\n");
        shell->next = env;
        shell->var = "SHELL";
        shell->value= KAEM_BINARY;
        env = shell;
    }

    /* Populate PATH variable
     * We don't need to calloc() because env_lookup() does this for us.
     */
    PATH = env_lookup("PATH");
    /* Populate USERNAME variable */
    char* USERNAME = env_lookup("LOGNAME");

    /* Handle edge cases */
    if((NULL == PATH) && (NULL == USERNAME))
    {
        /* We didn't find either of PATH or USERNAME -- use a generic PATH */
        PATH = calloc(MAX_STRING, sizeof(char));
        require(PATH != NULL, "Memory initialization of PATH failed\n");
        strcpy(PATH, "/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
    }
    else if(NULL == PATH)
    {
        /* We did find a username but not a PATH -- use a generic PATH but with /home/USERNAME */
        PATH = calloc(MAX_STRING, sizeof(char));
        PATH = strcat(PATH, "/home/");
        PATH = strcat(PATH, USERNAME);
        PATH = strcat(PATH, "/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games");
    }

    /* Open the script */
    script = fopen(filename, "r");

    if(NULL == script)
    {
        fputs("The file: ", stderr);
        fputs(filename, stderr);
        fputs(" can not be opened!\n", stderr);
        exit(EXIT_FAILURE);
    }

    /* Run the commands */
    run_script(script, argv);
    /* Cleanup */
    fclose(script);
    return EXIT_SUCCESS;
}

File /x86/kaem.run

Live-bootstrap source file is 'seed/stage0-posix/x86/kaem.run'.
URL: https://github.com/oriansj/stage0-posix-x86/blob/83508012c952e3aa3f70a89fd7d85d9a1af6f305/kaem.run
#! /usr/bin/env bash
# Mes --- Maxwell Equations of Software
# Copyright © 2017,2019 Jan Nieuwenhuizen <janneke@gnu.org>
# Copyright © 2017,2019 Jeremiah Orians
#
# This file is part of Mes.
#
# Mes is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or (at
# your option) any later version.
#
# Mes is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Mes.  If not, see <http://www.gnu.org/licenses/>.

# To run in kaem simply: kaem --verbose --strict

ARCH="x86"
ARCH_DIR="x86"
M2LIBC="../M2libc"
TOOLS="../${ARCH_DIR}/bin"
BLOOD_FLAG=" "
BASE_ADDRESS="0x08048000"
ENDIAN_FLAG="--little-endian"
BINDIR="../${ARCH_DIR}/bin"
BUILDDIR="../${ARCH_DIR}/artifact"
TMPDIR="${BUILDDIR}"
OPERATING_SYSTEM="Linux"

################################################
# Phase 12-15 Rebuild M2-Planet from C sources #
################################################
./${ARCH_DIR}/bin/kaem --verbose --strict --file ${ARCH_DIR}/mescc-tools-full-kaem.kaem

######################################################
# Phase 16-23 Build mescc-tools-extra from M2-Planet #
######################################################
cd mescc-tools-extra
${BINDIR}/kaem --verbose --strict --file mescc-tools-extra.kaem
cd ..
./${ARCH_DIR}/bin/sha256sum -c ${ARCH}.answers

#########################
# Load after.kaem hook  #
#########################
exec ./${ARCH_DIR}/bin/kaem --verbose --strict --file ./after.kaem

File /x86/mescc-tools-full-kaem.kaem

Live-bootstrap source file is 'seed/stage0-posix/x86/mescc-tools-full-kaem.kaem'.
URL: https://github.com/oriansj/stage0-posix-x86/blob/83508012c952e3aa3f70a89fd7d85d9a1af6f305/mescc-tools-full-kaem.kaem
#!/usr/bin/env bash
# Mes --- Maxwell Equations of Software
# Copyright © 2017,2019 Jan Nieuwenhuizen <janneke@gnu.org>
# Copyright © 2017,2019 Jeremiah Orians
#
# This file is part of Mes.
#
# Mes is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or (at
# your option) any later version.
#
# Mes is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Mes.  If not, see <http://www.gnu.org/licenses/>.

cd ${ARCH_DIR}

###############################################
# Phase-12 Build M2-Mesoplanet from M2-Planet #
###############################################

./artifact/M2 --architecture ${ARCH} \
    -f ../M2libc/sys/types.h \
    -f ../M2libc/stddef.h \
    -f ../M2libc/${ARCH}/linux/fcntl.c \
    -f ../M2libc/fcntl.c \
    -f ../M2libc/sys/utsname.h \
    -f ../M2libc/${ARCH}/linux/unistd.c \
    -f ../M2libc/${ARCH}/linux/sys/stat.c \
    -f ../M2libc/stdlib.c \
    -f ../M2libc/stdio.h \
    -f ../M2libc/stdio.c \
    -f ../M2libc/string.c \
    -f ../M2libc/bootstrappable.c \
    -f ../M2-Mesoplanet/cc.h \
    -f ../M2-Mesoplanet/cc_globals.c \
    -f ../M2-Mesoplanet/cc_env.c \
    -f ../M2-Mesoplanet/cc_reader.c \
    -f ../M2-Mesoplanet/cc_spawn.c \
    -f ../M2-Mesoplanet/cc_core.c \
    -f ../M2-Mesoplanet/cc_macro.c \
    -f ../M2-Mesoplanet/cc.c \
    --debug \
    -o ./artifact/M2-Mesoplanet-1.M1

./artifact/blood-elf-0 ${ENDIAN_FLAG} ${BLOOD_FLAG} -f ./artifact/M2-Mesoplanet-1.M1 -o ./artifact/M2-Mesoplanet-1-footer.M1

./bin/M1 --architecture ${ARCH} \
    ${ENDIAN_FLAG} \
    -f ../M2libc/${ARCH}/${ARCH}_defs.M1 \
    -f ../M2libc/${ARCH}/libc-full.M1 \
    -f ./artifact/M2-Mesoplanet-1.M1 \
    -f ./artifact/M2-Mesoplanet-1-footer.M1 \
    -o ./artifact/M2-Mesoplanet-1.hex2

./bin/hex2 --architecture ${ARCH} \
    ${ENDIAN_FLAG} \
    --base-address ${BASE_ADDRESS} \
    -f ../M2libc/${ARCH}/ELF-${ARCH}-debug.hex2 \
    -f ./artifact/M2-Mesoplanet-1.hex2 \
    -o ./bin/M2-Mesoplanet

#################################################
# Phase-13 Build final blood-elf from C sources #
#################################################

./artifact/M2 --architecture ${ARCH} \
    -f ../M2libc/sys/types.h \
    -f ../M2libc/stddef.h \
    -f ../M2libc/${ARCH}/linux/fcntl.c \
    -f ../M2libc/fcntl.c \
    -f ../M2libc/sys/utsname.h \
    -f ../M2libc/${ARCH}/linux/unistd.c \
    -f ../M2libc/stdlib.c \
    -f ../M2libc/stdio.h \
    -f ../M2libc/stdio.c \
    -f ../M2libc/bootstrappable.c \
    -f ../mescc-tools/stringify.c \
    -f ../mescc-tools/blood-elf.c \
    --debug \
    -o ./artifact/blood-elf-1.M1

./artifact/blood-elf-0 ${BLOOD_FLAG} ${ENDIAN_FLAG} -f ./artifact/blood-elf-1.M1 -o ./artifact/blood-elf-1-footer.M1
./bin/M1 --architecture ${ARCH} \
    ${ENDIAN_FLAG} \
    -f ../M2libc/${ARCH}/${ARCH}_defs.M1 \
    -f ../M2libc/${ARCH}/libc-full.M1 \
    -f ./artifact/blood-elf-1.M1 \
    -f ./artifact/blood-elf-1-footer.M1 \
    -o ./artifact/blood-elf-1.hex2

./bin/hex2 --architecture ${ARCH} \
    ${ENDIAN_FLAG} \
    --base-address ${BASE_ADDRESS} \
    -f ../M2libc/${ARCH}/ELF-${ARCH}-debug.hex2 \
    -f ./artifact/blood-elf-1.hex2 \
    -o ./bin/blood-elf

# Now we have our shipping debuggable blood-elf, the rest will be down hill from
# here as we have ALL of the core pieces of compiling and assembling debuggable
# programs in a debuggable form with corresponding C source code.

#############################################
# Phase-14 Build get_machine from C sources #
#############################################

./artifact/M2 --architecture ${ARCH} \
    -f ../M2libc/sys/types.h \
    -f ../M2libc/stddef.h \
    -f ../M2libc/sys/utsname.h \
    -f ../M2libc/${ARCH}/linux/unistd.c \
    -f ../M2libc/${ARCH}/linux/fcntl.c \
    -f ../M2libc/fcntl.c \
    -f ../M2libc/stdlib.c \
    -f ../M2libc/stdio.h \
    -f ../M2libc/stdio.c \
    -f ../M2libc/bootstrappable.c \
    -f ../mescc-tools/get_machine.c \
    --debug \
    -o artifact/get_machine.M1

./bin/blood-elf ${BLOOD_FLAG} ${ENDIAN_FLAG} -f ./artifact/get_machine.M1 -o ./artifact/get_machine-footer.M1

./bin/M1 --architecture ${ARCH} \
    ${ENDIAN_FLAG} \
    -f ../M2libc/${ARCH}/${ARCH}_defs.M1 \
    -f ../M2libc/${ARCH}/libc-full.M1 \
    -f ./artifact/get_machine.M1 \
    -f ./artifact/get_machine-footer.M1 \
    -o ./artifact/get_machine.hex2

./bin/hex2 --architecture ${ARCH} \
    ${ENDIAN_FLAG} \
    --base-address ${BASE_ADDRESS} \
    -f ../M2libc/${ARCH}/ELF-${ARCH}-debug.hex2 \
    -f ./artifact/get_machine.hex2 \
    -o ./bin/get_machine

############################################
# Phase-15 Build M2-Planet from M2-Planet  #
############################################

./artifact/M2 --architecture ${ARCH} \
    -f ../M2libc/sys/types.h \
    -f ../M2libc/stddef.h \
    -f ../M2libc/sys/utsname.h \
    -f ../M2libc/${ARCH}/linux/unistd.c \
    -f ../M2libc/${ARCH}/linux/fcntl.c \
    -f ../M2libc/fcntl.c \
    -f ../M2libc/stdlib.c \
    -f ../M2libc/stdio.h \
    -f ../M2libc/stdio.c \
    -f ../M2libc/bootstrappable.c \
    -f ../M2-Planet/cc.h \
    -f ../M2-Planet/cc_globals.c \
    -f ../M2-Planet/cc_reader.c \
    -f ../M2-Planet/cc_strings.c \
    -f ../M2-Planet/cc_types.c \
    -f ../M2-Planet/cc_core.c \
    -f ../M2-Planet/cc_macro.c \
    -f ../M2-Planet/cc.c \
    --debug \
    -o ./artifact/M2-1.M1

./bin/blood-elf ${ENDIAN_FLAG} ${BLOOD_FLAG} -f ./artifact/M2-1.M1 -o ./artifact/M2-1-footer.M1

./bin/M1 --architecture ${ARCH} \
    ${ENDIAN_FLAG} \
    -f ../M2libc/${ARCH}/${ARCH}_defs.M1 \
    -f ../M2libc/${ARCH}/libc-full.M1 \
    -f ./artifact/M2-1.M1 \
    -f ./artifact/M2-1-footer.M1 \
    -o ./artifact/M2-1.hex2

./bin/hex2 --architecture ${ARCH} \
    ${ENDIAN_FLAG} \
    --base-address ${BASE_ADDRESS} \
    -f ../M2libc/${ARCH}/ELF-${ARCH}-debug.hex2 \
    -f ./artifact/M2-1.hex2 \
    -o ./bin/M2-Planet

cd ..

File /M2-Mesoplanet/cc.h

Live-bootstrap source file is 'seed/stage0-posix/M2-Mesoplanet/cc.h'.
URL: https://github.com/oriansj/M2-Mesoplanet/blob/2935751e3b74f468aa846fff844ce96f62d575ac/cc.h
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>


#define FALSE 0
#define TRUE 1


int in_set(int c, char* s);
int match(char* a, char* b);
void require(int bool, char* error);
char* int2str(int x, int base, int signed_p);
void reset_hold_string();


struct type
{
    struct type* next;
    int size;
    int offset;
    int is_signed;
    struct type* indirect;
    struct type* members;
    struct type* type;
    char* name;
};

struct token_list
{
    struct token_list* next;
    union
    {
        struct token_list* locals;
        struct token_list* prev;
    };
    char* s;
    union
    {
        struct type* type;
        char* filename;
    };
    union
    {
        struct token_list* arguments;
        struct token_list* expansion;
        int depth;
        int linenumber;
    };
};

#include "cc_globals.h"

File /M2-Mesoplanet/cc_globals.c

Live-bootstrap source file is 'seed/stage0-posix/M2-Mesoplanet/cc_globals.c'.
URL: https://github.com/oriansj/M2-Mesoplanet/blob/2935751e3b74f468aa846fff844ce96f62d575ac/cc_globals.c
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

/* What types we have */
struct type* global_types;
struct type* prim_types;

/* What we are currently working on */
struct token_list* global_token;

/* Output reorder collections*/
struct token_list* output_list;
struct token_list* strings_list;
struct token_list* globals_list;

/* Make our string collection more efficient */
char* hold_string;
int string_index;

int MAX_STRING;

/* enable preprocessor-only mode */
int PREPROCESSOR_MODE;

/* enable spawn behavior to be effective */
char* M2LIBC_PATH;
char* Architecture;
char* OperatingSystem;
int WORDSIZE;
int ENDIAN;
char* BASEADDRESS;
int STDIO_USED;
char* TEMPDIR;

/* So we don't shoot ourself in the face */
int FUZZING;
int DIRTY_MODE;
int DEBUG_LEVEL;

File /M2-Mesoplanet/cc_env.c

Live-bootstrap source file is 'seed/stage0-posix/M2-Mesoplanet/cc_env.c'.
URL: https://github.com/oriansj/M2-Mesoplanet/blob/2935751e3b74f468aa846fff844ce96f62d575ac/cc_env.c
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include"cc.h"
#include <sys/utsname.h>

void init_macro_env(char* sym, char* value, char* source, int num);
char* env_lookup(char* variable);
void clear_string(char* s);

struct utsname* get_uname_data()
{
    struct utsname* unameData = calloc(1, sizeof(struct utsname));
    require(NULL != unameData, "unameData calloc failed\n");
    uname(unameData);
    if(4 <= DEBUG_LEVEL)
    {
        fputs("utsname details: ", stderr);
        fputs(unameData->sysname, stderr);
        fputc(' ', stderr);
        fputs(unameData->machine, stderr);
        fputc('\n', stderr);
    }
    return unameData;
}

void setup_env()
{
    if(2 <= DEBUG_LEVEL) fputs("Starting setup_env\n", stderr);
    char* ARCH;
    if(NULL != Architecture)
    {
        ARCH = Architecture;
    }
    else
    {
        ARCH = NULL;
        struct utsname* unameData = get_uname_data();

        if(match("i386", unameData->machine) ||
           match("i486", unameData->machine) ||
           match("i586", unameData->machine) ||
           match("i686", unameData->machine) ||
           match("i686-pae", unameData->machine)) ARCH = "x86";
        else if(match("x86_64", unameData->machine)) ARCH = "amd64";
        else ARCH = unameData->machine;
        if(3 <= DEBUG_LEVEL)
        {
            fputs("Architecture selected: ", stderr);
            fputs(ARCH, stderr);
            fputc('\n', stderr);
        }

        /* Check for override */
        char* hold = env_lookup("ARCHITECTURE_OVERRIDE");
        if(NULL != hold)
        {
            ARCH = hold;
            if(3 <= DEBUG_LEVEL)
            {
                fputs("environmental override for ARCH: ", stderr);
                fputs(ARCH, stderr);
                fputc('\n', stderr);
            }
        }
        free(unameData);
    }


    /* Set desired architecture */
    WORDSIZE = 32;
    ENDIAN = FALSE;
    BASEADDRESS = "0x0";
    if(match("knight-native", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using knight-native architecture\n", stderr);
        ENDIAN = TRUE;
        Architecture = "knight-native";
    }
    else if(match("knight-posix", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using knight-posix architecture\n", stderr);
        ENDIAN = TRUE;
        Architecture = "knight-posix";
    }
    else if(match("x86", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using x86 architecture\n", stderr);
        BASEADDRESS = "0x8048000";
        Architecture = "x86";
        init_macro_env("__i386__", "1", "--architecture", 0);
    }
    else if(match("amd64", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using amd64 architecture\n", stderr);
        BASEADDRESS = "0x00600000";
        Architecture = "amd64";
        WORDSIZE = 64;
        init_macro_env("__x86_64__", "1", "--architecture", 0);
    }
    else if(match("armv7l", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using armv7l architecture\n", stderr);
        BASEADDRESS = "0x10000";
        Architecture = "armv7l";
        init_macro_env("__arm__", "1", "--architecture", 0);
    }
    else if(match("aarch64", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using aarch64 architecture\n", stderr);
        BASEADDRESS = "0x400000";
        Architecture = "aarch64";
        WORDSIZE = 64;
        init_macro_env("__aarch64__", "1", "--architecture", 0);
    }
    else if(match("riscv32", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using riscv32 architecture\n", stderr);
        BASEADDRESS = "0x600000";
        Architecture = "riscv32";
        init_macro_env("__riscv", "1", "--architecture", 0);
        init_macro_env("__riscv_xlen", "32", "--architecture", 1);
    }
    else if(match("riscv64", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using riscv64 architecture\n", stderr);
        BASEADDRESS = "0x600000";
        Architecture = "riscv64";
        WORDSIZE = 64;
        init_macro_env("__riscv", "1", "--architecture", 0);
        init_macro_env("__riscv_xlen", "64", "--architecture", 1);
    }
    else
    {
        fputs("Unknown architecture: ", stderr);
        fputs(ARCH, stderr);
        fputs(" know values are: knight-native, knight-posix, x86, amd64, armv7l, aarch64, riscv32 and riscv64\n", stderr);
        exit(EXIT_FAILURE);
    }


    /* Setup Operating System */
    if(NULL == OperatingSystem)
    {
        OperatingSystem = "Linux";
        if(3 <= DEBUG_LEVEL)
        {
            fputs("Operating System selected: ", stderr);
            fputs(OperatingSystem, stderr);
            fputc('\n', stderr);
        }

        /* Check for override */
        char* hold = env_lookup("OS_OVERRIDE");
        if(NULL != hold)
        {
            OperatingSystem = hold;
            if(3 <= DEBUG_LEVEL)
            {
                fputs("environmental override for OS: ", stderr);
                fputs(OperatingSystem, stderr);
                fputc('\n', stderr);
            }
        }
    }

    if(match("UEFI", OperatingSystem))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using UEFI\n", stderr);
        BASEADDRESS = "0x0";
        OperatingSystem = "UEFI";
        init_macro_env("__uefi__", "1", "--os", 0);
    }

    if(2 <= DEBUG_LEVEL) fputs("setup_env successful\n", stderr);
}

struct Token
{
    /*
     * For the token linked-list, this stores the token; for the env linked-list
     * this stores the value of the variable.
     */
    char* value;
    /*
     * Used only for the env linked-list. It holds a string containing the
     * name of the var.
     */
    char* var;
    /*
     * This struct stores a node of a singly linked list, store the pointer to
     * the next node.
     */
    struct Token* next;
};

struct Token* env;

int array_length(char** array)
{
    int length = 0;

    while(array[length] != NULL)
    {
        length = length + 1;
    }

    return length;
}

/* Search for a variable in the token linked-list */
char* token_lookup(char* variable, struct Token* token)
{
    if(6 <= DEBUG_LEVEL)
    {
        fputs("in token_lookup\nLooking for: ", stderr);
        fputs(variable, stderr);
        fputc('\n', stderr);
    }
    /* Start at the head */
    struct Token* n = token;

    /* Loop over the linked-list */
    while(n != NULL)
    {
        if(15 <= DEBUG_LEVEL)
        {
            fputs(n->var, stderr);
            fputc('\n', stderr);
        }
        if(match(variable, n->var))
        {
            if(6 <= DEBUG_LEVEL) fputs("match found in token_lookup\n", stderr);
            /* We have found the correct node */
            return n->value; /* Done */
        }

        /* Nope, try the next */
        n = n->next;
    }

    /* We didn't find anything! */
    return NULL;
}

/* Search for a variable in the env linked-list */
char* env_lookup(char* variable)
{
    return token_lookup(variable, env);
}

char* envp_hold;
int envp_index;

void reset_envp_hold()
{
    clear_string(envp_hold);
    envp_index = 0;
}

void push_env_byte(int c)
{
    envp_hold[envp_index] = c;
    envp_index = envp_index + 1;
    require(4096 > envp_index, "Token exceeded 4096 char envp limit\n");
}

struct Token* process_env_variable(char* envp_line, struct Token* n)
{
    struct Token* node = calloc(1, sizeof(struct Token));
    require(node != NULL, "Memory initialization of node failed\n");
    reset_envp_hold();
    int i = 0;

    while(envp_line[i] != '=')
    {
        /* Copy over everything up to = to var */
        push_env_byte(envp_line[i]);
        i = i + 1;
    }

    node->var = calloc(i + 2, sizeof(char));
    require(node->var != NULL, "Memory initialization of n->var in population of env failed\n");
    strcpy(node->var, envp_hold);

    i = i + 1; /* Skip over = */

    reset_envp_hold();
    while(envp_line[i] != 0)
    {
        /* Copy everything else to value */
        push_env_byte(envp_line[i]);
        i = i + 1;
    }

    /* Sometimes, we get lines like VAR=, indicating nothing is in the variable */
    if(0 == strlen(envp_hold))
    {
        node->value = "";
    }
    else
    {
        /* but looks like we got something so, lets use it */
        node->value = calloc(strlen(envp_hold) + 2, sizeof(char));
        require(node->value != NULL, "Memory initialization of n->var in population of env failed\n");
        strcpy(node->value, envp_hold);
    }

    node->next = n;
    return node;
}

void populate_env(char** envp)
{
    if(2 <= DEBUG_LEVEL) fputs("populate_env started\n", stderr);
    /* You can't populate a NULL environment */
    if(NULL == envp)
    {
        if(3 <= DEBUG_LEVEL) fputs("NULL envp\n", stderr);
        return;
    }

    /* avoid empty arrays */
    int max = array_length(envp);

    if(0 == max)
    {
        if(3 <= DEBUG_LEVEL) fputs("Empty envp\n", stderr);
        return;
    }

    /* Initialize env and n */
    env = NULL;
    int i;
    envp_hold = calloc(4096, sizeof(char));
    require(envp_hold != NULL, "Memory initialization of envp_hold in population of env failed\n");
    char* envp_line = calloc(4096, sizeof(char));
    require(envp_line != NULL, "Memory initialization of envp_line in population of env failed\n");

    if(3 <= DEBUG_LEVEL) fputs("starting env loop\n", stderr);
    for(i = 0; i < max; i = i + 1)
    {
        /*
         * envp is weird.
         * When referencing envp[i]'s characters directly, they were all jumbled.
         * So just copy envp[i] to envp_line, and work with that - that seems
         * to fix it.
         */
        clear_string(envp_line);
        require(4096 > strlen(envp[i]), "envp line exceeds 4096byte limit\n");
        strcpy(envp_line, envp[i]);

        if(9 <= DEBUG_LEVEL)
        {
            fputs("trying envp_line: ", stderr);
            fputs(envp_line, stderr);
            fputc('\n', stderr);
        }

        env = process_env_variable(envp_line, env);

        if(9 <= DEBUG_LEVEL)
        {
            fputs("got var of: ", stderr);
            fputs(env->var, stderr);
            fputs("\nAnd value of: ", stderr);
            fputs(env->value, stderr);
            fputc('\n', stderr);
        }
    }

    free(envp_line);
    free(envp_hold);
    if(3 <= DEBUG_LEVEL)
    {
        fputs("\n\nenv loop successful\n", stderr);
        fputs(int2str(i, 10, FALSE), stderr);
        fputs(" envp records processed\n\n", stderr);
    }

    require(NULL != env, "can't have an empty environment from the creation of a non-null environment\n");
    if(2 <= DEBUG_LEVEL) fputs("populate_env successful\n", stderr);
}

File /M2-Mesoplanet/cc_reader.c

Live-bootstrap source file is 'seed/stage0-posix/M2-Mesoplanet/cc_reader.c'.
URL: https://github.com/oriansj/M2-Mesoplanet/blob/2935751e3b74f468aa846fff844ce96f62d575ac/cc_reader.c
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "cc.h"
char* env_lookup(char* variable);
char* int2str(int x, int base, int signed_p);

struct visited
{
    struct visited* prev;
    char* name;
};

/* Globals */
FILE* input;
struct token_list* token;
int line;
char* file;
struct visited* vision;

int previously_seen(char* s)
{
    struct visited* v = vision;
    while(NULL != v)
    {
        if(match(v->name, s)) return TRUE;
        v = v->prev;
    }
    return FALSE;
}

void just_seen(char* s)
{
    struct visited* hold = calloc(1, sizeof(struct visited));
    hold->prev = vision;
    hold->name = s;
    vision = hold;
}

int grab_byte()
{
    int c = fgetc(input);
    if(10 == c) line = line + 1;
    return c;
}

void push_byte(int c)
{
    hold_string[string_index] = c;
    string_index = string_index + 1;
    require(MAX_STRING > string_index, "Token exceeded MAX_STRING char limit\nuse --max-string number to increase\n");
}

int consume_byte(int c)
{
    push_byte(c);
    return grab_byte();
}

int preserve_string(int c)
{
    int frequent = c;
    int escape = FALSE;
    do
    {
        if(!escape && '\\' == c ) escape = TRUE;
        else escape = FALSE;
        c = consume_byte(c);
        require(EOF != c, "Unterminated string\n");
    } while(escape || (c != frequent));
    c = consume_byte(frequent);
    return c;
}


void copy_string(char* target, char* source, int max)
{
    int i = 0;
    while(0 != source[i])
    {
        target[i] = source[i];
        i = i + 1;
        if(i == max) break;
    }
}


int preserve_keyword(int c, char* S)
{
    while(in_set(c, S))
    {
        c = consume_byte(c);
    }
    return c;
}

void clear_string(char* s)
{
    int i = 0;
    while(0 != s[i])
    {
        s[i] = 0;
        i = i + 1;
        require(i < MAX_STRING, "string exceeded max string size while clearing string\n");
    }
}

void reset_hold_string()
{
    clear_string(hold_string);
    string_index = 0;
}


/* note if this is the first token in the list, head needs fixing up */
struct token_list* eat_token(struct token_list* token)
{
    if(NULL != token->prev)
    {
        token->prev->next = token->next;
    }

    /* update backlinks */
    if(NULL != token->next)
    {
        token->next->prev = token->prev;
    }

    return token->next;
}


void new_token(char* s, int size)
{
    struct token_list* current = calloc(1, sizeof(struct token_list));
    require(NULL != current, "Exhausted memory while getting token\n");

    /* More efficiently allocate memory for string */
    current->s = calloc(size, sizeof(char));
    require(NULL != current->s, "Exhausted memory while trying to copy a token\n");
    copy_string(current->s, s, MAX_STRING);

    current->prev = token;
    current->next = token;
    current->linenumber = line;
    current->filename = file;
    token = current;
}

int get_token(int c)
{
    reset_hold_string();

    if(c == EOF)
    {
        return c;
    }
    else if((32 == c) || (9 == c) || (c == '\n'))
    {
        c = consume_byte(c);
    }
    else if('#' == c)
    {
        c = consume_byte(c);
        c = preserve_keyword(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
    }
    else if(in_set(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"))
    {
        c = preserve_keyword(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_:");
    }
    else if(in_set(c, "<=>|&!^%"))
    {
        c = preserve_keyword(c, "<=>|&!^%");
    }
    else if(in_set(c, "'\""))
    {
        c = preserve_string(c);
    }
    else if(c == '/')
    {
        c = consume_byte(c);
        if(c == '*')
        {
            c = consume_byte(c);
            while(c != '/')
            {
                while(c != '*')
                {
                    c = consume_byte(c);
                    require(EOF != c, "Hit EOF inside of block comment\n");
                }
                c = consume_byte(c);
                require(EOF != c, "Hit EOF inside of block comment\n");
            }
            c = consume_byte(c);
        }
        else if(c == '/')
        {
            while(c != '\n')
            {
                c = consume_byte(c);
                require(EOF != c, "Hit EOF inside of line comment\n");
            }
            c = consume_byte(c);
        }
        else if(c == '=')
        {
            c = consume_byte(c);
        }
    }
    else if(c == '*')
    {
        c = consume_byte(c);
        if(c == '=')
        {
            c = consume_byte(c);
        }
    }
    else if(c == '+')
    {
        c = consume_byte(c);
        if(c == '=')
        {
            c = consume_byte(c);
        }
        if(c == '+')
        {
            c = consume_byte(c);
        }
    }
    else if(c == '-')
    {
        c = consume_byte(c);
        if(c == '=')
        {
            c = consume_byte(c);
        }
        if(c == '>')
        {
            c = consume_byte(c);
        }
        if(c == '-')
        {
            c = consume_byte(c);
        }
    }
    else
    {
        c = consume_byte(c);
    }

    return c;
}

struct token_list* reverse_list(struct token_list* head)
{
    struct token_list* root = NULL;
    struct token_list* next;
    while(NULL != head)
    {
        next = head->next;
        head->next = root;
        root = head;
        head = next;
    }
    return root;
}

int read_include(int c)
{
    reset_hold_string();
    int done = FALSE;
    int ch;

    while(!done)
    {
        if(c == EOF)
        {
            fputs("we don't support EOF as a filename in #include statements\n", stderr);
            exit(EXIT_FAILURE);
        }
        else if((32 == c) || (9 == c) || (c == '\n'))
        {
            c = grab_byte();
        }
        else if(('"' == c) || ('<' == c))
        {
            if('<' == c) c = '>';
            ch = c;
            do
            {
                c = consume_byte(c);
                require(EOF != c, "Unterminated filename in #include\n");
            } while(c != ch);
            if('>' == ch) hold_string[0] = '<';
            done = TRUE;
        }
    }

    return c;
}

void insert_file_header(char* name, int line)
{
    char* hold_line = int2str(line, 10, FALSE);
    reset_hold_string();
    strcat(hold_string, "// #FILENAME ");
    strcat(hold_string, name);
    strcat(hold_string, " ");
    strcat(hold_string, hold_line);
    new_token(hold_string, strlen(hold_string)+2);
    new_token("\n", 3);
}

struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename, int include);
int include_file(int ch, int include_file)
{
    /* The old state to restore to */
    char* hold_filename = file;
    FILE* hold_input = input;
    int hold_number;

    /* The new file to load */
    char* new_filename;
    FILE* new_file;

    require(EOF != ch, "#include failed to receive filename\n");
    /* Remove the #include */
    token = token->next;

    /* Get new filename */
    read_include(ch);
    /* with just a little extra to put in the matching at the end */
    new_token(hold_string, string_index + 3);

    ch = '\n';
    new_filename = token->s;
    /* Remove name from stream */
    token = token->next;

    /* Try to open the file */
    if('<' == new_filename[0])
    {
        if(match("stdio.h", new_filename + 1)) STDIO_USED = TRUE;
        reset_hold_string();
        strcat(hold_string, M2LIBC_PATH);
        strcat(hold_string, "/");
        strcat(hold_string, new_filename + 1);
        strcat(new_filename, ">");
        if(match("Linux", OperatingSystem))
        {
            if(NULL == strstr(hold_string, "uefi"))
            {
                new_file = fopen(hold_string, "r");
            }
            else
            {
                puts("skipping:");
                puts(hold_string);
                return ch;
            }
        }
        else if(match("UEFI", OperatingSystem))
        {
            if(NULL == strstr(hold_string, "linux"))
            {
                new_file = fopen(hold_string, "r");
            }
            else
            {
                puts("skipping:");
                puts(hold_string);
                return ch;
            }
        }
        else
        {
            puts("unknown host");
            exit(EXIT_FAILURE);
        }
    }
    else
    {
        if(match("M2libc/bootstrappable.h", new_filename+1))
        {
            reset_hold_string();
            strcat(hold_string, M2LIBC_PATH);
            strcat(hold_string, "/bootstrappable.h");
            new_file = fopen(hold_string, "r");
        }
        else new_file = fopen(new_filename+1, "r");

        strcat(new_filename, "\"");
    }

    /* prevent multiple visits */
    if(previously_seen(new_filename)) return ch;
    just_seen(new_filename);

    /* special case this compatibility crap */
    if(match("\"../gcc_req.h\"", new_filename) || match("\"gcc_req.h\"", new_filename)) return ch;

    if(include_file)
    {
        fputs("reading file: ", stderr);
        fputs(new_filename, stderr);
        fputc('\n', stderr);
    }

    /* catch garbage input */
    if(NULL == new_file)
    {
        fputs("unable to read file: ", stderr);
        fputs(new_filename, stderr);
        fputs("\nAborting hard!\n", stderr);
        exit(EXIT_FAILURE);
    }

    /* protect our current line number */
    hold_number = line + 1;

    /* Read the new file */
    if(include_file) read_all_tokens(new_file, token, new_filename, include_file);

    /* put back old file info */
    insert_file_header(hold_filename, hold_number);

    /* resume reading old file */
    input = hold_input;
    line = hold_number;
    file = hold_filename;
    return ch;
}

struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename, int include)
{
    token = current;
    insert_file_header(filename, 1);
    input  = a;
    line = 1;
    file = filename;
    int ch = grab_byte();
    while(EOF != ch)
    {
        ch = get_token(ch);
        new_token(hold_string, string_index + 2);
        if(match("#include", token->s)) ch = include_file(ch, include);
    }

    return token;
}

File /M2-Mesoplanet/cc_spawn.c

Live-bootstrap source file is 'seed/stage0-posix/M2-Mesoplanet/cc_spawn.c'.
URL: https://github.com/oriansj/M2-Mesoplanet/blob/2935751e3b74f468aa846fff844ce96f62d575ac/cc_spawn.c
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include"cc.h"
#include <unistd.h>
#include <sys/wait.h>
#define MAX_ARRAY 256

char* env_lookup(char* variable);

/* Function to find a character in a string */
char* find_char(char* string, char a)
{
    if(0 == string[0])
    {
        return NULL;
    }

    while(a != string[0])
    {
        string = string + 1;

        if(0 == string[0])
        {
            return string;
        }
    }

    return string;
}

/* Find the full path to an executable */
char* find_executable(char* name)
{
    char* PATH = env_lookup("PATH");
    require(NULL != PATH, "No PATH found\nAborting\n");
    if(match("", name))
    {
        return NULL;
    }

    if(('.' == name[0]) || ('/' == name[0]))
    {
        /* assume names that start with . or / are relative or absolute */
        return name;
    }

    char* trial = calloc(MAX_STRING, sizeof(char));
    char* MPATH = calloc(MAX_STRING, sizeof(char)); /* Modified PATH */
    require(MPATH != NULL, "Memory initialization of MPATH in find_executable failed\n");
    strcpy(MPATH, PATH);
    FILE* t;
    char* next = find_char(MPATH, ':');
    int index;
    int offset;
    int mpath_length;
    int name_length;
    int trial_length;

    while(NULL != next)
    {
        /* Reset trial */
        trial_length = strlen(trial);

        for(index = 0; index < trial_length; index = index + 1)
        {
            trial[index] = 0;
        }

        next[0] = 0;
        /* prepend_string(MPATH, prepend_string("/", name)) */
        mpath_length = strlen(MPATH);

        for(index = 0; index < mpath_length; index = index + 1)
        {
            require(MAX_STRING > index, "Element of PATH is too long\n");
            trial[index] = MPATH[index];
        }

        trial[index] = '/';
        offset = strlen(trial);
        name_length = strlen(name);

        for(index = 0; index < name_length; index = index + 1)
        {
            require(MAX_STRING > index, "Element of PATH is too long\n");
            trial[index + offset] = name[index];
        }

        /* Try the trial */
        trial_length = strlen(trial);
        require(trial_length < MAX_STRING, "COMMAND TOO LONG!\nABORTING HARD\n");
        t = fopen(trial, "r");

        if(NULL != t)
        {
            fclose(t);
            return trial;
        }

        MPATH = next + 1;
        next = find_char(MPATH, ':');
    }

    return NULL;
}

void sanity_command_check(char** array)
{
    int i = 0;
    char* s = array[0];
    while(NULL != s)
    {
        fputs(s, stderr);
        fputc(' ', stderr);
        i = i + 1;
        s = array[i];
    }
    fputc('\n', stderr);
}


int what_exit(char* program, int status)
{
    /***********************************************************************************
     * If the low-order 8 bits of w_status are equal to 0x7F or zero, the child        *
     * process has stopped. If the low-order 8 bits of w_status are non-zero and are   *
     * not equal to 0x7F, the child process terminated due to a signal otherwise, the  *
     * child process terminated due to an exit() call.                                 *
     *                                                                                 *
     * In the event it was a signal that stopped the process the top 8 bits of         *
     * w_status contain the signal that caused the process to stop.                    *
     *                                                                                 *
     * In the event it was terminated the bottom 7 bits of w_status contain the        *
     * terminating error number for the process.                                       *
     *                                                                                 *
     * If bit 0x80 of w_status is set, a core dump was produced.                       *
     ***********************************************************************************/

    if(DEBUG_LEVEL > 6)
    {
        fputs("in what_exit with char* program of: ", stderr);
        fputs(program, stderr);
        fputs("\nAnd int status of: 0x", stderr);
        fputs(int2str(status, 16, FALSE), stderr);
        fputc('\n', stderr);
    }

    int WIFEXITED = !(status & 0x7F);
    int WEXITSTATUS = (status & 0xFF00) >> 8;
    int WTERMSIG = status & 0x7F;
    int WCOREDUMP = status & 0x80;
    int WIFSIGNALED = !((0x7F == WTERMSIG) || (0 == WTERMSIG));
    int WIFSTOPPED = ((0x7F == WTERMSIG) && (0 == WCOREDUMP));

    if(WIFEXITED)
    {
        if(DEBUG_LEVEL > 2)
        {
            fputc('\n', stderr);
            fputs(program, stderr);
            fputs(" normal termination, exit status = ", stderr);
            fputs(int2str(WEXITSTATUS, 10, TRUE), stderr);
            fputc('\n', stderr);
        }
        return WEXITSTATUS;
    }
    else if (WIFSIGNALED)
    {
        fputc('\n', stderr);
        fputs(program, stderr);
        fputs(" abnormal termination, signal number = ", stderr);
        fputs(int2str(WTERMSIG, 10, TRUE), stderr);
        fputc('\n', stderr);
        if(WCOREDUMP) fputs("core dumped\n", stderr);
        return WTERMSIG;
    }
    else if(WIFSTOPPED)
    {
        fputc('\n', stderr);
        fputs(program, stderr);
        fputs(" child stopped, signal number = ", stderr);
        fputs(int2str(WEXITSTATUS, 10, TRUE), stderr);
        fputc('\n', stderr);
        return WEXITSTATUS;
    }

    fputc('\n', stderr);
    fputs(program, stderr);
    fputs(" :: something crazy happened with execve\nI'm just gonna get the hell out of here\n", stderr);
    exit(EXIT_FAILURE);
}


void _execute(char* name, char** array, char** envp)
{
    int status; /* i.e. return code */
    /* Get the full path to the executable */
    char* program = find_executable(name);

    /* Check we can find the executable */
    if(NULL == program)
    {
        fputs("WHILE EXECUTING ", stderr);
        fputs(name, stderr);
        fputs(" NOT FOUND!\nABORTING HARD\n", stderr);
        exit(EXIT_FAILURE);
    }

    sanity_command_check(array);

    int result;
#ifdef __uefi__
    result = spawn(program, array, envp);
#else
    int f = fork();

    /* Ensure fork succeeded */
    if(f == -1)
    {
        fputs("WHILE EXECUTING ", stderr);
        fputs(name, stderr);
        fputs("fork() FAILED\nABORTING HARD\n", stderr);
        exit(EXIT_FAILURE);
    }
    else if(f == 0)
    {
        /* Child */
        /**************************************************************
         * Fuzzing produces random stuff; we don't want it running    *
         * dangerous commands. So we just don't execve.               *
         **************************************************************/
        if(FALSE == FUZZING)
        {
            /* We are not fuzzing */
            /* execve() returns only on error */
            execve(program, array, envp);
            fputs("Unable to execute: ", stderr);
            fputs(program, stderr);
            fputs("\nPlease check file permissions and that it is a valid binary\n", stderr);
        }

        /* Prevent infinite loops */
        _exit(EXIT_FAILURE);
    }

    /* Otherwise we are the parent */
    /* And we should wait for it to complete */
    waitpid(f, &status, 0);

    result = what_exit(program, status);
#endif
    if(0 != result)
    {
        fputs("Subprocess: ", stderr);
        fputs(program, stderr);
        fputs(" error\nAborting for safety\n", stderr);
        exit(result);
    }
}

void insert_array(char** array, int index, char* string)
{
    int size = strlen(string);
    array[index] = calloc(size+2, sizeof(char));
    strcpy(array[index], string);
}


void spawn_hex2(char* input, char* output, char* architecture, char** envp, int debug)
{
    char* hex2;
#ifdef __uefi__
    hex2 = "hex2.efi";
#else
    hex2 = "hex2";
#endif

    char* elf_header = calloc(MAX_STRING, sizeof(char));
    elf_header = strcat(elf_header, M2LIBC_PATH);
    elf_header = strcat(elf_header, "/");
    elf_header = strcat(elf_header, architecture);
    if(match("UEFI", OperatingSystem)) elf_header = strcat(elf_header, "/uefi/PE32-");
    else elf_header = strcat(elf_header, "/ELF-");
    elf_header = strcat(elf_header, architecture);
    if(debug)
    {
        elf_header = strcat(elf_header, "-debug.hex2");
    }
    else
    {
        elf_header = strcat(elf_header, ".hex2");
    }

    fputs("# starting hex2 linking\n", stdout);
    char** array = calloc(MAX_ARRAY, sizeof(char*));
    insert_array(array, 0, hex2);
    insert_array(array, 1, "--file");
    insert_array(array, 2, elf_header);
    insert_array(array, 3, "--file");
    insert_array(array, 4, input);
    insert_array(array, 5, "--output");
    insert_array(array, 6, output);
    insert_array(array, 7, "--architecture");
    insert_array(array, 8, architecture);
    insert_array(array, 9, "--base-address");
    insert_array(array, 10, BASEADDRESS);
    if(ENDIAN)
    {
        insert_array(array, 11, "--big-endian");
    }
    else
    {
        insert_array(array, 11, "--little-endian");
    }
    _execute(hex2, array, envp);
}


void spawn_M1(char* input, char* debug_file, char* output, char* architecture, char** envp, int debug_flag)
{
    char* M1;
#ifdef __uefi__
    M1 = "M1.efi";
#else
    M1 = "M1";
#endif

    fputs("# starting M1 assembly\n", stdout);
    char* definitions = calloc(MAX_STRING, sizeof(char));
    definitions = strcat(definitions, M2LIBC_PATH);
    definitions = strcat(definitions, "/");
    definitions = strcat(definitions, architecture);
    definitions = strcat(definitions, "/");
    definitions = strcat(definitions, architecture);
    definitions = strcat(definitions, "_defs.M1");

    char* libc = calloc(MAX_STRING, sizeof(char));
    libc = strcat(libc, M2LIBC_PATH);
    libc = strcat(libc, "/");
    libc = strcat(libc, architecture);

    if(match("UEFI", OperatingSystem))
    {
        libc = strcat(libc, "/uefi/libc-full.M1");
    }
    else if(STDIO_USED)
    {
        libc = strcat(libc, "/libc-full.M1");
    }
    else
    {
        libc = strcat(libc, "/libc-core.M1");
    }

    char** array = calloc(MAX_ARRAY, sizeof(char*));
    insert_array(array, 0, M1);
    insert_array(array, 1, "--file");
    insert_array(array, 2, definitions);
    insert_array(array, 3, "--file");
    insert_array(array, 4, libc);
    insert_array(array, 5, "--file");
    insert_array(array, 6, input);
    if(ENDIAN)
    {
        insert_array(array, 7, "--big-endian");
    }
    else
    {
        insert_array(array, 7, "--little-endian");
    }
    insert_array(array, 8, "--architecture");
    insert_array(array, 9, architecture);

    if(debug_flag)
    {
        insert_array(array, 10, "--file");
        insert_array(array, 11, debug_file);
        insert_array(array, 12, "--output");
        insert_array(array, 13, output);
    }
    else
    {
        insert_array(array, 10, "--output");
        insert_array(array, 11, output);
    }
    _execute(M1, array, envp);
}


void spawn_blood_elf(char* input, char* output, char** envp, int large_flag)
{
    char* blood_elf;
#ifdef __uefi__
    blood_elf = "blood-elf.efi";
#else
    blood_elf = "blood-elf";
#endif

    fputs("# starting Blood-elf stub generation\n", stdout);
    char** array = calloc(MAX_ARRAY, sizeof(char*));
    insert_array(array, 0,blood_elf);
    insert_array(array, 1, "--file");
    insert_array(array, 2, input);
    if(ENDIAN)
    {
        insert_array(array, 3, "--big-endian");
    }
    else
    {
        insert_array(array, 3, "--little-endian");
    }
    insert_array(array, 4, "--output");
    insert_array(array, 5, output);
    if(large_flag) insert_array(array, 6, "--64");
    _execute(blood_elf, array, envp);
}

void spawn_M2(char* input, char* output, char* architecture, char** envp, int debug_flag)
{
    char* M2_Planet;
#ifdef __uefi__
    M2_Planet = "M2-Planet.efi";
#else
    M2_Planet = "M2-Planet";
#endif

    fputs("# starting M2-Planet build\n", stdout);
    char** array = calloc(MAX_ARRAY, sizeof(char*));
    insert_array(array, 0, M2_Planet);
    insert_array(array, 1, "--file");
    insert_array(array, 2, input);
    insert_array(array, 3, "--output");
    insert_array(array, 4, output);
    insert_array(array, 5, "--architecture");
    insert_array(array, 6, architecture);
    if(debug_flag) insert_array(array, 7, "--debug");
    _execute(M2_Planet, array, envp);
}

void spawn_processes(int debug_flag, char* prefix, char* preprocessed_file, char* destination, char** envp)
{
    int large_flag = FALSE;
    if(WORDSIZE > 32) large_flag = TRUE;

    if(match("UEFI", OperatingSystem))
    {
        debug_flag = FALSE;
    }

    char* M2_output = calloc(100, sizeof(char));
    strcpy(M2_output, prefix);
    strcat(M2_output, "/M2-Planet-XXXXXX");
    int i = mkstemp(M2_output);
    if(-1 != i)
    {
        close(i);
        spawn_M2(preprocessed_file, M2_output, Architecture, envp, debug_flag);
    }
    else
    {
        fputs("unable to get a tempfile for M2-Planet output\n", stderr);
        exit(EXIT_FAILURE);
    }

    char* blood_output = "";
    if(debug_flag)
    {
        blood_output = calloc(100, sizeof(char));
        strcpy(blood_output, prefix);
        strcat(blood_output, "/blood-elf-XXXXXX");
        i = mkstemp(blood_output);
        if(-1 != i)
        {
            close(i);
            spawn_blood_elf(M2_output, blood_output, envp, large_flag);
        }
        else
        {
            fputs("unable to get a tempfile for blood-elf output\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    char* M1_output = calloc(100, sizeof(char));
    strcpy(M1_output, prefix);
    strcat(M1_output, "/M1-macro-XXXXXX");
    i = mkstemp(M1_output);
    if(-1 != i)
    {
        close(i);
        spawn_M1(M2_output, blood_output, M1_output, Architecture, envp, debug_flag);
    }
    else
    {
        fputs("unable to get a tempfile for M1 output\n", stderr);
        exit(EXIT_FAILURE);
    }

    /* We no longer need the M2-Planet tempfile output */
    if(!DIRTY_MODE) remove(M2_output);
    /* Nor the blood-elf output anymore if it exists */
    if(!match("", blood_output))
    {
        if(!DIRTY_MODE) remove(blood_output);
    }

    /* Build the final binary */
    spawn_hex2(M1_output, destination, Architecture, envp, debug_flag);

    /* clean up after ourselves*/
    if(!DIRTY_MODE) remove(M1_output);
}

File /M2-Mesoplanet/cc_core.c

Live-bootstrap source file is 'seed/stage0-posix/M2-Mesoplanet/cc_core.c'.
URL: https://github.com/oriansj/M2-Mesoplanet/blob/2935751e3b74f468aa846fff844ce96f62d575ac/cc_core.c
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "cc.h"
#include "gcc_req.h"
#include <stdint.h>

/* Imported functions */
char* int2str(int x, int base, int signed_p);

void line_error_token(struct token_list *token)
{
    if(NULL == token)
    {
        fputs("EOF reached inside of line_error\n", stderr);
        fputs("problem at end of file\n", stderr);
        return;
    }
    fputs(token->filename, stderr);
    fputs(":", stderr);
    fputs(int2str(token->linenumber, 10, TRUE), stderr);
    fputs(":", stderr);
}

void line_error()
{
    line_error_token(global_token);
}

void require_match(char* message, char* required)
{
    require(NULL != global_token, "EOF reached inside of require match\n");
    if(!match(global_token->s, required))
    {
        line_error();
        fputs(message, stderr);
        exit(EXIT_FAILURE);
    }
    global_token = global_token->next;
    require(NULL != global_token, "EOF after require match occurred\n");
}

void output_tokens(struct token_list *i, FILE* out)
{
    while(NULL != i)
    {
        fputs(i->s, out);
        i = i->next;
    }
}

File /M2-Mesoplanet/cc_macro.c

Live-bootstrap source file is 'seed/stage0-posix/M2-Mesoplanet/cc_macro.c'.
URL: https://github.com/oriansj/M2-Mesoplanet/blob/2935751e3b74f468aa846fff844ce96f62d575ac/cc_macro.c
/* Copyright (C) 2021 Sanne Wouda
 * Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
 * Copyright (C) 2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "cc.h"
#include "gcc_req.h"

void require(int bool, char* error);
int strtoint(char* a);
void line_error_token(struct token_list* list);
struct token_list* eat_token(struct token_list* head);
struct token_list* reverse_list(struct token_list* head);

struct conditional_inclusion
{
    struct conditional_inclusion* prev;
    int include; /* 1 == include, 0 == skip */
    int previous_condition_matched; /* 1 == all subsequent conditions treated as FALSE */
};

struct macro_list
{
    struct macro_list* next;
    char* symbol;
    struct token_list* expansion;
    struct token_list* arguments;
};

struct macro_list* macro_env;
struct conditional_inclusion* conditional_inclusion_top;

/* point where we are currently modifying the global_token list */
struct token_list* macro_token;

void init_macro_env(char* sym, char* value, char* source, int num)
{
    struct macro_list* hold = macro_env;
    macro_env = calloc(1, sizeof(struct macro_list));
    macro_env->symbol = sym;
    macro_env->next = hold;
    macro_env->expansion = calloc(1, sizeof(struct token_list));
    macro_env->expansion->s = value;
    macro_env->expansion->filename = source;
    macro_env->expansion->linenumber = num;
}

void _eat_current_token(int eat_whitespace)
{
    int update_global_token = FALSE;
    if (macro_token == global_token)
        update_global_token = TRUE;

    macro_token = eat_token(macro_token);
    if(eat_whitespace)
    {
        while (macro_token->s[0] == ' ')
        macro_token = eat_token(macro_token);
    }

    if(update_global_token)
        global_token = macro_token;
}

void eat_current_token()
{
    _eat_current_token(TRUE);
}

void eat_current_token_without_space()
{
    _eat_current_token(FALSE);
}

struct token_list* lookup_token(struct token_list* token, struct token_list* arguments)
{
    if(NULL == token)
    {
        fputs("null token received in token\n", stderr);
        exit(EXIT_FAILURE);
    }

    struct token_list* hold = arguments;

    while (NULL != hold)
    {
        if (match(token->s, hold->s))
        {
            /* found! */
            return hold->expansion;
        }

        hold = hold->next;
    }

    /* not found! */
    return NULL;
}

/* returns the first token inserted; inserts *before* point */
struct token_list* insert_tokens(struct token_list* point, struct token_list* token)
{
    struct token_list* copy;
    struct token_list* first = NULL;

    while (NULL != token)
    {
        copy = calloc(1, sizeof(struct token_list));
        copy->s = token->s;
        copy->filename = token->filename;
        copy->linenumber = token->linenumber;

        if(NULL == first)
        {
            first = copy;
        }

        copy->next = point;

        if (NULL != point)
        {
            copy->prev = point->prev;

            if(NULL != point->prev)
            {
                point->prev->next = copy;
            }

            point->prev = copy;
        }

        token = token->next;
    }

    return first;
}

/* returns the first token inserted; inserts *before* point */
struct token_list* copy_list(struct token_list* token)
{
    struct token_list* copy;
    struct token_list* prev = NULL;

    while (NULL != token)
    {
        copy = calloc(1, sizeof(struct token_list));
        copy->s = token->s;

        copy->next = prev;
        copy->prev = prev;
        prev = copy;
        token = token->next;
    }
    copy = reverse_list(copy);

    return copy;
}

struct macro_list* lookup_macro(struct token_list* token)
{
    if(NULL == token)
    {
        line_error_token(macro_token);
        fputs("null token received in lookup_macro\n", stderr);
        exit(EXIT_FAILURE);
    }

    struct macro_list* hold = macro_env;

    while (NULL != hold)
    {
        if (match(token->s, hold->symbol))
        {
            /* found! */
            return hold;
        }

        hold = hold->next;
    }

    /* not found! */
    return NULL;
}

void remove_macro(struct token_list* token)
{
    if(NULL == token)
    {
        line_error_token(macro_token);
        fputs("received a null in remove_macro\n", stderr);
        exit(EXIT_FAILURE);
    }

    struct macro_list* hold = macro_env;
    struct macro_list* temp;

    /* Deal with the first element */
    if (match(token->s, hold->symbol)) {
        macro_env = hold->next;
        free(hold);
        return;
    }

    /* Remove element form the middle of linked list */
    while (NULL != hold->next)
    {
        if (match(token->s, hold->next->symbol))
        {
            temp = hold->next;
            hold->next = hold->next->next;
            free(temp);
            return;
        }

        hold = hold->next;
    }

    /* nothing to undefine */
    return;
}

int macro_expression();
int macro_variable()
{
    int value = 0;
    struct macro_list* hold = lookup_macro(macro_token);
    if (NULL != hold)
    {
        if(NULL == hold->expansion)
        {
            line_error_token(macro_token);
            fputs("hold->expansion is a null\n", stderr);
            exit(EXIT_FAILURE);
        }
        value = strtoint(hold->expansion->s);
    }
    eat_current_token();
    return value;
}

int macro_number()
{
    int result = strtoint(macro_token->s);
    eat_current_token();
    return result;
}

int macro_primary_expr()
{
    int defined_has_paren = FALSE;
    int hold;
    require(NULL != macro_token, "got an EOF terminated macro primary expression\n");

    if('-' == macro_token->s[0])
    {
        eat_current_token();
        return -macro_primary_expr();
    }
    else if('!' == macro_token->s[0])
    {
        eat_current_token();
        return !macro_primary_expr();
    }
    else if('(' == macro_token->s[0])
    {
        eat_current_token();
        hold = macro_expression();
        require(')' == macro_token->s[0], "missing ) in macro expression\n");
        eat_current_token();
        return hold;
    }
    else if(match("defined", macro_token->s))
    {
        eat_current_token();

        require(NULL != macro_token, "got an EOF terminated macro defined expression\n");

        if('(' == macro_token->s[0])
        {
            defined_has_paren = TRUE;
            eat_current_token();
        }

        if (NULL != lookup_macro(macro_token))
        {
            hold = TRUE;
        }
        else
        {
            hold = FALSE;
        }
        eat_current_token();

        if(TRUE == defined_has_paren)
        {
            if(NULL == macro_token)
            {
                line_error_token(macro_token);
                fputs("unterminated define ( statement\n", stderr);
                exit(EXIT_FAILURE);
            }
            require(')' == macro_token->s[0], "missing close parenthesis for defined()\n");
            eat_current_token();
        }

        return hold;
    }
    else if(in_set(macro_token->s[0], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"))
    {
        return macro_variable();
    }
    else if(in_set(macro_token->s[0], "0123456789"))
    {
        return macro_number();
    }
    else
    {
        return 0;    /* FIXME: error handling */
    }
}

int macro_additive_expr()
{
    int lhs = macro_primary_expr();
    int hold;

    require(NULL != macro_token, "got an EOF terminated macro additive expression\n");
    if(match("+", macro_token->s))
    {
        eat_current_token();
        return lhs + macro_additive_expr();
    }
    else if(match("-", macro_token->s))
    {
        eat_current_token();
        return lhs - macro_additive_expr();
    }
    else if(match("*", macro_token->s))
    {
        eat_current_token();
        return lhs * macro_additive_expr();
    }
    else if(match("/", macro_token->s))
    {
        eat_current_token();
        hold = macro_additive_expr();
        require(0 != hold, "divide by zero not valid even in C macros\n");
        return lhs / hold;
    }
    else if(match("%", macro_token->s))
    {
        eat_current_token();
        hold = macro_additive_expr();
        require(0 != hold, "modulus by zero not valid even in C macros\n");
        return lhs % hold;
    }
    else if(match(">>", macro_token->s))
    {
        eat_current_token();
        return lhs >> macro_additive_expr();
    }
    else if(match("<<", macro_token->s))
    {
        eat_current_token();
        return lhs << macro_additive_expr();
    }
    else
    {
        return lhs;
    }
}

int macro_relational_expr()
{
    int lhs = macro_additive_expr();

    if(match("<", macro_token->s))
    {
        eat_current_token();
        return lhs < macro_relational_expr();
    }
    else if(match("<=", macro_token->s))
    {
        eat_current_token();
        return lhs <= macro_relational_expr();
    }
    else if(match(">=", macro_token->s))
    {
        eat_current_token();
        return lhs >= macro_relational_expr();
    }
    else if(match(">", macro_token->s))
    {
        eat_current_token();
        return lhs > macro_relational_expr();
    }
    else if(match("==", macro_token->s))
    {
        eat_current_token();
        return lhs == macro_relational_expr();
    }
    else if(match("!=", macro_token->s))
    {
        eat_current_token();
        return lhs != macro_relational_expr();
    }
    else
    {
        return lhs;
    }
}

int macro_bitwise_expr()
{
    int rhs;
    int lhs = macro_relational_expr();

    if(match("&", macro_token->s))
    {
        eat_current_token();
        return lhs & macro_bitwise_expr();
    }
    else if(match("&&", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs && rhs;
    }
    else if(match("|", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs | rhs;
    }
    else if(match("||", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs || rhs;
    }
    else if(match("^", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs ^ rhs;
    }
    else
    {
        return lhs;
    }
}

int macro_expression()
{
    return macro_bitwise_expr();
}

void handle_define()
{
    struct macro_list* hold;
    struct token_list* arg;
    struct token_list* expansion_end = NULL;

    /* don't use #define statements from non-included blocks */
    int conditional_define = TRUE;
    if(NULL != conditional_inclusion_top)
    {
        if(FALSE == conditional_inclusion_top->include)
        {
            conditional_define = FALSE;
        }
    }

    eat_current_token();

    require(NULL != macro_token, "got an EOF terminated #define\n");
    require('\n' != macro_token->s[0], "unexpected newline after #define\n");

    /* insert new macro */
    hold = calloc(1, sizeof(struct macro_list));
    hold->symbol = macro_token->s;
    hold->next = macro_env;
    /* provided it isn't in a non-included block */
    if(conditional_define) macro_env = hold;

    /* discard the macro name */
    eat_current_token_without_space();

    /* Handle macro arguments */
    if(macro_token->s[0] == '(')
    {
        /* discard ( */
        eat_current_token();
        require(NULL != macro_token, "got an EOF terminated #define\n");
        if(macro_token->s[0] != ')')
        {
            arg = calloc(1, sizeof(struct token_list));
            arg->s = macro_token->s;
            hold->arguments = arg;
            eat_current_token();
            require(NULL != macro_token, "incomplete macro call\n");
            while(macro_token->s[0] == ',')
            {
                eat_current_token();
                require(NULL != macro_token, "incomplete macro call, got an EOF instead of an argument\n");
                arg = calloc(1, sizeof(struct token_list));
                arg->s = macro_token->s;
                arg->next = hold->arguments;
                hold->arguments = arg;
                eat_current_token();
                require(NULL != macro_token, "incomplete macro call\n");
            }
        }
        eat_current_token();

        /* Reverse argument list */
        hold->arguments = reverse_list(hold->arguments);

        require(NULL != macro_token, "got an EOF terminated #define\n");
    }
    else if(macro_token->s[0] == ' ')
    {
        eat_current_token();
    }

    while (TRUE)
    {
        require(NULL != macro_token, "got an EOF terminated #define\n");

        if ('\n' == macro_token->s[0])
        {
            if(NULL == expansion_end)
            {
                hold->expansion = NULL;
                expansion_end = macro_token;
                return;
            }
            expansion_end->next = NULL;
            return;
        }
        else if(('/' == macro_token->s[0]) && ('*' == macro_token->s[1]))
        {
            eat_current_token();
            continue;
        }
        else if(('/' == macro_token->s[0]) && ('/' == macro_token->s[1]))
        {
            macro_token->s = "\n";
            if(NULL == expansion_end)
            {
                hold->expansion = NULL;
                expansion_end = macro_token;
                return;
            }
            expansion_end->next = NULL;
            return;
        }

        if(NULL == hold)
        {
            eat_current_token();
            continue;
        }

        expansion_end = macro_token;

        /* in the first iteration, we set the first token of the expansion, if
           it exists */
        if (NULL == hold->expansion)
        {
            hold->expansion = macro_token;
        }

        /* throw away if not used */
        if(!conditional_define && (NULL != hold))
        {
            free(hold);
            hold = NULL;
        }

        eat_current_token();
    }
}

void handle_undef()
{
    eat_current_token();
    remove_macro(macro_token);
    eat_current_token();
}

void handle_error(int warning_p)
{
    /* don't use #error statements from non-included blocks */
    int conditional_error = TRUE;
    if(NULL != conditional_inclusion_top)
    {
        if(FALSE == conditional_inclusion_top->include)
        {
            conditional_error = FALSE;
        }
    }
    eat_current_token();
    /* provided it isn't in a non-included block */
    if(conditional_error)
    {
        line_error_token(macro_token);
        if(warning_p) fputs(" warning: #warning ", stderr);
        else fputs(" error: #error ", stderr);
        while (TRUE)
        {
            if ('\n' == macro_token->s[0]) break;
            fputs(macro_token->s, stderr);
            macro_token = macro_token->next;
        }
        fputs("\n", stderr);
        if(!warning_p) exit(EXIT_FAILURE);
    }
    while (TRUE)
    {
        /* discard the error */
        if ('\n' == macro_token->s[0])
        {
            return;
        }
        eat_current_token();
    }
}

void macro_directive()
{
    struct conditional_inclusion *t;
    int result;

    /* FIXME: whitespace is allowed between "#"" and "if" */
    if(match("#if", macro_token->s))
    {
        eat_current_token();
        /* evaluate constant integer expression */
        result = macro_expression();
        /* push conditional inclusion */
        t = calloc(1, sizeof(struct conditional_inclusion));
        t->prev = conditional_inclusion_top;
        conditional_inclusion_top = t;
        t->include = TRUE;

        if(FALSE == result)
        {
            t->include = FALSE;
        }

        t->previous_condition_matched = t->include;
    }
    else if(match("#ifdef", macro_token->s))
    {
        eat_current_token();
        require(NULL != macro_token, "got an EOF terminated macro defined expression\n");
        if (NULL != lookup_macro(macro_token))
        {
            result = TRUE;
        }
        else
        {
            result = FALSE;
        }
        eat_current_token();

        /* push conditional inclusion */
        t = calloc(1, sizeof(struct conditional_inclusion));
        t->prev = conditional_inclusion_top;
        conditional_inclusion_top = t;
        t->include = TRUE;

        if(FALSE == result)
        {
            t->include = FALSE;
        }

        t->previous_condition_matched = t->include;
    }
    else if(match("#ifndef", macro_token->s))
    {
        eat_current_token();
        require(NULL != macro_token, "got an EOF terminated macro defined expression\n");
        if (NULL != lookup_macro(macro_token))
        {
            result = FALSE;
        }
        else
        {
            result = TRUE;
        }
        eat_current_token();

        /* push conditional inclusion */
        t = calloc(1, sizeof(struct conditional_inclusion));
        t->prev = conditional_inclusion_top;
        conditional_inclusion_top = t;
        t->include = TRUE;

        if(FALSE == result)
        {
            t->include = FALSE;
        }

        t->previous_condition_matched = t->include;
    }
    else if(match("#elif", macro_token->s))
    {
        eat_current_token();
        result = macro_expression();
        require(NULL != conditional_inclusion_top, "#elif without leading #if\n");
        conditional_inclusion_top->include = result && !conditional_inclusion_top->previous_condition_matched;
        conditional_inclusion_top->previous_condition_matched =
            conditional_inclusion_top->previous_condition_matched || conditional_inclusion_top->include;
    }
    else if(match("#else", macro_token->s))
    {
        eat_current_token();
        require(NULL != conditional_inclusion_top, "#else without leading #if\n");
        conditional_inclusion_top->include = !conditional_inclusion_top->previous_condition_matched;
    }
    else if(match("#endif", macro_token->s))
    {
        if(NULL == conditional_inclusion_top)
        {
            line_error_token(macro_token);
            fputs("unexpected #endif\n", stderr);
            exit(EXIT_FAILURE);
        }

        eat_current_token();
        /* pop conditional inclusion */
        t = conditional_inclusion_top;
        conditional_inclusion_top = conditional_inclusion_top->prev;
        free(t);
    }
    else if(match("#define", macro_token->s))
    {
        handle_define();
    }
    else if(match("#undef", macro_token->s))
    {
        handle_undef();
    }
    else if(match("#error", macro_token->s))
    {
        handle_error(FALSE);
    }
    else if(match("#warning", macro_token->s))
    {
        handle_error(TRUE);
    }
    else if(match("#FILENAME", macro_token->s))
    {
        while(TRUE)
        {
            if(NULL == macro_token)
            {
                return;
            }

            if('\n' == macro_token->s[0])
            {
                return;
            }

            eat_current_token();
        }
    }
    else
    {
        /* Put a big fat warning but see if we can just ignore */
        fputs(">>WARNING<<\n>>WARNING<<\n", stderr);
        line_error_token(macro_token);
        fputs("feature: ", stderr);
        fputs(macro_token->s, stderr);
        fputs(" unsupported in M2-Planet\nIgnoring line, may result in bugs\n>>WARNING<<\n>>WARNING<<\n\n", stderr);

        /* unhandled macro directive; let's eat until a newline; om nom nom */
        while(TRUE)
        {
            if(NULL == macro_token)
            {
                return;
            }

            if('\n' == macro_token->s[0])
            {
                return;
            }

            eat_current_token();
        }
    }
}

struct token_list* expand_macro_functions(struct token_list* expansion, struct token_list* arguments)
{
    struct token_list* expanded_token;
    struct token_list* head;
    struct token_list* hold; /* Same as head unless head == NULL */
    head = copy_list(expansion);
    while(NULL != head)
    {
        expanded_token = lookup_token(head, arguments);
        hold = head;
        if(NULL != expanded_token)
        {
            insert_tokens(head, expanded_token);
            hold = head->prev;
            head = eat_token(head);
        }
        else
        {
            head = head->next;
        }
    }
    while(NULL != hold->prev) hold = hold->prev;
    return hold;
}

void eat_until_endif()
{
    /* This #if block is nested inside of an #if block that needs to be dropped, lose EVERYTHING */
    do
    {
        if(match("#if", macro_token->s) || match("#ifdef", macro_token->s) || match("#ifndef", macro_token->s))
        {
            eat_current_token();
            eat_until_endif();
        }

        eat_current_token();
        require(NULL != macro_token, "Unterminated #if block\n");
    } while(!match("#endif", macro_token->s));
}

void eat_block()
{
    /* This conditional #if block is wrong, drop everything until the #elif/#else/#endif */
    do
    {
        if(match("#if", macro_token->s) || match("#ifdef", macro_token->s) || match("#ifndef", macro_token->s))
        {
            eat_current_token();
            eat_until_endif();
        }

        eat_current_token();
        require(NULL != macro_token, "Unterminated #if block\n");
    } while(!match("#elif", macro_token->s) && !match("#else", macro_token->s) && !match("#endif", macro_token->s));
}

struct token_list* maybe_expand(struct token_list* token)
{
    if(NULL == token)
    {
        line_error_token(macro_token);
        fputs("maybe_expand passed a null token\n", stderr);
        exit(EXIT_FAILURE);
    }

    struct macro_list* hold = lookup_macro(token);
    struct token_list* hold2;
    struct token_list* hold3;
    struct token_list* hold4;
    if(NULL == token->next)
    {
        line_error_token(macro_token);
        fputs("we can't expand a null token: ", stderr);
        fputs(token->s, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }

    if (NULL == hold)
    {
        return token->next;
    }

    if(match("__M2__", token->s)) return token->next;

    token = eat_token(token);

    if (NULL == hold->expansion)
    {
        return token->next;
    }

    /* Match macro arguments with stored names */
    hold3 = hold->arguments;
    if(NULL != hold3)
    {
        if(token->s[0] == ' ')
        {
            token = eat_token(token);
        }
        require('(' == token->s[0], "missing open parenthesis for macro function\n");
        token = eat_token(token);
        require(NULL != token, "got an EOF terminated macro function\n");
        do
        {
            hold2 = calloc(1, sizeof(struct token_list));
            hold2->s = token->s;
            hold2->next = hold->arguments->expansion;
            hold->arguments->expansion = hold2;
            token = eat_token(token);
            require(NULL != token, "incomplete macro call\n");
            if(token->s[0] == ',')
            {
                hold->arguments->expansion = reverse_list(hold->arguments->expansion);
                hold->arguments = hold->arguments->next;
                require(NULL != hold->arguments, "too many arguments in macro call\n");
                token = eat_token(token);
                require(NULL != token, "incomplete macro call\n");
            }
        } while(token->s[0] != ')');
        hold->arguments->expansion = reverse_list(hold->arguments->expansion);
        hold->arguments = hold3;
        token = eat_token(token);
    }
    hold4 = expand_macro_functions(hold->expansion, hold->arguments);
    hold4 = insert_tokens(token, hold4);

    return hold4;
}

void preprocess()
{
    int start_of_line = TRUE;
    macro_token = global_token;

    while(NULL != macro_token)
    {
        if(start_of_line && '#' == macro_token->s[0])
        {
            macro_directive();

            if(macro_token)
            {
                if('\n' != macro_token->s[0])
                {
                    line_error_token(macro_token);
                    fputs("newline expected at end of macro directive\n", stderr);
                    fputs("found: '", stderr);
                    fputs(macro_token->s, stderr);
                    fputs("'\n", stderr);
                    exit(EXIT_FAILURE);
                }
            }
        }
        else if('\n' == macro_token->s[0])
        {
            start_of_line = TRUE;
            macro_token = macro_token->next;
        }
        else
        {
            start_of_line = FALSE;
            if(NULL == conditional_inclusion_top)
            {
                macro_token = maybe_expand(macro_token);
            }
            else if(!conditional_inclusion_top->include)
            {
                /* rewrite the token stream to exclude the current token */
                eat_block();
                start_of_line = TRUE;
            }
            else
            {
                macro_token = maybe_expand(macro_token);
            }
        }
    }
}

File /M2-Mesoplanet/cc.c

Live-bootstrap source file is 'seed/stage0-posix/M2-Mesoplanet/cc.c'.
URL: https://github.com/oriansj/M2-Mesoplanet/blob/2935751e3b74f468aa846fff844ce96f62d575ac/cc.c
/* Copyright (C) 2016, 2021 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * Copyright (C) 2020 Gabriel Wicki
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include"cc.h"
#include <unistd.h>

/* The core functions */
void populate_env(char** envp);
void setup_env();
char* env_lookup(char* variable);
void initialize_types();
struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename, int include);
struct token_list* reverse_list(struct token_list* head);

void init_macro_env(char* sym, char* value, char* source, int num);
void preprocess();
void output_tokens(struct token_list *i, FILE* out);
int strtoint(char *a);
void spawn_processes(int debug_flag, char* prefix, char* preprocessed_file, char* destination, char** envp);

int follow_includes;

void prechecks(int argc, char** argv)
{
    int env = 0;
    char* hold;
    int i = 1;
    while(i <= argc)
    {
        if(NULL == argv[i])
        {
            i += 1;
        }
        else if(match(argv[i], "--debug-mode"))
        {
            hold = argv[i+1];
            require(NULL != hold, "--debug-mode requires an argument\n");
            DEBUG_LEVEL = strtoint(hold);
            if(0 == DEBUG_LEVEL)
            {
                require(match("0", hold), "--debug-mode values must be numbers\n"
                                          "and level 0 needed to be expressed as 0\n");
            }
            fputs("DEBUG_LEVEL set to: ", stderr);
            fputs(hold, stderr);
            fputc('\n', stderr);
            i+= 2;
        }
        else if(match(argv[i], "-A") || match(argv[i], "--architecture"))
        {
            hold = argv[i+1];
            require(NULL != hold, "--architecture needs to be passed an architecture\n");
            Architecture = hold;
            i += 2;
        }
        else if(match(argv[i], "--os") || match(argv[i], "--operating-system"))
        {
            hold = argv[i+1];
            require(NULL != hold, "--operating-system needs to be passed an operating system\n");
            OperatingSystem = hold;
            i += 2;
        }
        else if(match(argv[i], "--max-string"))
        {
            hold = argv[i+1];
            require(NULL != hold, "--max-string requires a numeric argument\n");
            MAX_STRING = strtoint(hold);
            require(0 < MAX_STRING, "Not a valid string size\nAbort and fix your --max-string\n");
            i += 2;
        }
        else if(match(argv[i], "--no-includes"))
        {
            follow_includes = FALSE;
            i+= 1;
        }
        else if(match(argv[i], "-I"))
        {
            hold = argv[i+1];
            if(NULL == hold)
            {
                fputs("-I requires a PATH\n", stderr);
                exit(EXIT_FAILURE);
            }
            if(1 <= DEBUG_LEVEL)
            {
                fputs("M2LIBC_PATH set by -I to ", stderr);
                fputs(hold, stderr);
                fputc('\n', stderr);
            }
            M2LIBC_PATH = hold;
            i += 2;
        }
        else if(match(argv[i], "-D"))
        {
            hold = argv[i+1];
            if(NULL == hold)
            {
                fputs("-D requires an argument", stderr);
                exit(EXIT_FAILURE);
            }
            while(0 != hold[0])
            {
                if('=' == hold[0])
                {
                    hold[0] = 0;
                    hold = hold + 1;
                    break;
                }
                hold = hold + 1;
            }
            init_macro_env(argv[i+1], hold, "__ARGV__", env);
            env = env + 1;
            i += 2;
        }
        else
        {
            i += 1;
        }
    }
}

int main(int argc, char** argv, char** envp)
{
    /****************************************************************************
     * Zero means no debugging messages and larger positive values means more   *
     * chatty output. Level 15 means EVERYTHING but 7 should cover most magic   *
     ****************************************************************************/
    DEBUG_LEVEL = 0;
    /* Setup __M2__ (It is very very special *DO NOT MESS WITH IT* ) */
    init_macro_env("__M2__", "__M2__", "__INTERNAL_M2__", 0);

    /* Our fun globals */
    FUZZING = FALSE;
    MAX_STRING = 65536;
    PREPROCESSOR_MODE = FALSE;
    STDIO_USED = FALSE;
    DIRTY_MODE = FALSE;
    Architecture = NULL;
    OperatingSystem = NULL;

    /* Our fun locals */
    int debug_flag = TRUE;
    FILE* in = stdin;
    FILE* tempfile;
    char* destination_name = "a.out";
    FILE* destination_file = stdout;
    char* name;
    int DUMP_MODE = FALSE;
    follow_includes = TRUE;

    /* Try to get our needed updates */
    prechecks(argc, argv);

    /* Get the environmental bits */
    if(1 <= DEBUG_LEVEL) fputs("Starting to setup Environment\n", stderr);
    populate_env(envp);
    setup_env();
    if(1 <= DEBUG_LEVEL) fputs("Environment setup\n", stderr);

    M2LIBC_PATH = env_lookup("M2LIBC_PATH");
    if(NULL == M2LIBC_PATH) M2LIBC_PATH = "./M2libc";
    else if(1 <= DEBUG_LEVEL)
    {
        fputs("M2LIBC_PATH set by environment variable to ", stderr);
        fputs(M2LIBC_PATH, stderr);
        fputc('\n', stderr);
    }

    TEMPDIR = env_lookup("TMPDIR");
    if(NULL == TEMPDIR) TEMPDIR = "/tmp";
    else if(1 <= DEBUG_LEVEL)
    {
        fputs("TEMPDIR set by environment variable to ", stderr);
        fputs(TEMPDIR, stderr);
        fputc('\n', stderr);
    }

    int i = 1;
    while(i <= argc)
    {
        if(NULL == argv[i])
        {
            i += 1;
        }
        else if(match(argv[i], "-E") || match(argv[i], "--preprocess-only"))
        {
            PREPROCESSOR_MODE = TRUE;
            i += 1;
        }
        else if(match(argv[i], "--dump-mode"))
        {
            DUMP_MODE = TRUE;
            i+= 1;
        }
        else if(match(argv[i], "--dirty-mode"))
        {
            DIRTY_MODE = TRUE;
            i+= 1;
        }
        else if(match(argv[i], "--no-includes"))
        {
            /* Handled by precheck*/
            i+= 1;
        }
        else if(match(argv[i], "--debug-mode"))
        {
            /* Handled by precheck */
            i+= 2;
        }
        else if(match(argv[i], "-A") || match(argv[i], "--architecture"))
        {
            /* Handled by precheck */
            i += 2;
        }
        else if(match(argv[i], "--os") || match(argv[i], "--operating-system"))
        {
            /* Handled by precheck */
            i += 2;
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            if(NULL == hold_string)
            {
                hold_string = calloc(MAX_STRING + 4, sizeof(char));
                require(NULL != hold_string, "Impossible Exhaustion has occured\n");
            }

            name = argv[i + 1];
            if(NULL == name)
            {
                fputs("did not receive a file name\n", stderr);
                exit(EXIT_FAILURE);
            }

            in = fopen(name, "r");
            if(NULL == in)
            {
                fputs("Unable to open for reading file: ", stderr);
                fputs(name, stderr);
                fputs("\n Aborting to avoid problems\n", stderr);
                exit(EXIT_FAILURE);
            }
            global_token = read_all_tokens(in, global_token, name, follow_includes);
            fclose(in);
            i += 2;
        }
        else if(match(argv[i], "-o") || match(argv[i], "--output"))
        {
            destination_name = argv[i + 1];
            require(NULL != destination_name, "--output option requires a filename to follow\n");
            destination_file = fopen(destination_name, "w");
            if(NULL == destination_file)
            {
                fputs("Unable to open for writing file: ", stderr);
                fputs(argv[i + 1], stderr);
                fputs("\n Aborting to avoid problems\n", stderr);
                exit(EXIT_FAILURE);
            }
            i += 2;
        }
        else if(match(argv[i], "--max-string"))
        {
            /* handled by precheck */
            i += 2;
        }
        else if(match(argv[i], "-I"))
        {
            /* Handled by precheck */
            i += 2;
        }
        else if(match(argv[i], "-D"))
        {
            /* Handled by precheck */
            i += 2;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs(" -f input file\n -o output file\n --help for this message\n --version for file version\n-E or --preprocess-only\n--max-string N (N is a number)\n--fuzz\n--no-debug\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-V") || match(argv[i], "--version"))
        {
            fputs("M2-Mesoplanet v1.11.0\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "--fuzz"))
        {
            /* Set fuzzing */
            FUZZING = TRUE;
            i += 1;
        }
        else if(match(argv[i], "--no-debug"))
        {
            /* strip things down */
            debug_flag = FALSE;
            i += 1;
        }
        else if(match(argv[i], "--temp-directory"))
        {
            name = argv[i+1];
            if(NULL == name)
            {
                fputs("--temp-directory requires a PATH\n", stderr);
                exit(EXIT_FAILURE);
            }
            if(1 <= DEBUG_LEVEL)
            {
                fputs("TEMPDIR set by --temp-directory to ", stderr);
                fputs(name, stderr);
                fputc('\n', stderr);
            }
            TEMPDIR = name;
            i += 2;
        }
        else
        {
            if(5 <= DEBUG_LEVEL)
            {
                fputs("on index: ", stderr);
                fputs(int2str(i, 10, TRUE), stderr);
                fputc('\n', stderr);
            }
            fputs("UNKNOWN ARGUMENT: ", stdout);
            fputs(argv[i], stdout);
            fputc('\n', stdout);
            exit(EXIT_FAILURE);
        }
    }

    if(1 <= DEBUG_LEVEL) fputs("READ all files\n", stderr);

    /* Deal with special case of wanting to read from standard input */
    if(stdin == in)
    {
        hold_string = calloc(MAX_STRING, sizeof(char));
        require(NULL != hold_string, "Impossible Exhaustion has occured\n");
        global_token = read_all_tokens(in, global_token, "STDIN", follow_includes);
    }

    if(NULL == global_token)
    {
        fputs("Either no input files were given or they were empty\n", stderr);
        exit(EXIT_FAILURE);
    }

    if(1 <= DEBUG_LEVEL) fputs("Start to reverse list\n", stderr);
    global_token = reverse_list(global_token);
    if(1 <= DEBUG_LEVEL) fputs("List reversed\n", stderr);

    if(DUMP_MODE)
    {
        output_tokens(global_token, destination_file);
        exit(EXIT_SUCCESS);
    }

    preprocess();

    if(PREPROCESSOR_MODE)
    {
        fputs("/* M2-Mesoplanet Preprocessed source */\n", destination_file);
        output_tokens(global_token, destination_file);
        fclose(destination_file);
    }
    else
    {
        /* Ensure we can write to the temp directory */
        int permissions = access(TEMPDIR, 0);
        if(0 != permissions)
        {
            fputs("unable to access: ", stderr);
            fputs(TEMPDIR, stderr);
            fputs(" for use as a temp directory\nPlease use --temp-directory to set a directory you can use or set the TMPDIR variable\n", stderr);
            exit(EXIT_FAILURE);
        }

        name = calloc(100, sizeof(char));
        strcpy(name, TEMPDIR);
        strcat(name, "/M2-Mesoplanet-XXXXXX");
        i = mkstemp(name);
        tempfile = fdopen(i, "w");
        if(NULL != tempfile)
        {
            /* Our preprocessed crap */
            output_tokens(global_token, tempfile);
            fclose(tempfile);

            /* Make me a real binary */
            spawn_processes(debug_flag, TEMPDIR, name, destination_name, envp);

            /* And clean up the donkey */
            if(!DIRTY_MODE) remove(name);
        }
        else
        {
            fputs("unable to get a tempfile for M2-Mesoplanet output\n", stderr);
            exit(EXIT_FAILURE);
        }
    }
    return EXIT_SUCCESS;
}

File /mescc-tools/get_machine.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools/get_machine.c'.
URL: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=get_machine.c;id=08d5a0679fa4dc00babe02306f1bc50703083389
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
 * This file is part of mescc-tools.
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */


#include <stdio.h>
#include <stdlib.h>
#include <sys/utsname.h>
int match(char* a, char* b);

#define TRUE 1
//CONSTANT TRUE 1
#define FALSE 0
//CONSTANT FALSE 0

/* Standard C main program */
int main(int argc, char **argv)
{
    int exact = FALSE;
    int override = FALSE;
    char* override_string;
    int option_index = 1;

    struct utsname* unameData = calloc(1, sizeof(struct utsname));
    uname(unameData);

    while(option_index <= argc)
    {
        if(NULL == argv[option_index])
        {
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--exact"))
        {
            exact = TRUE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--override"))
        {
            override = TRUE;
            if((option_index + 1) < argc)
            {
                override_string = argv[option_index + 1];
                option_index = option_index + 2;
            }
            else
            {
                fputs("--override requires an actual override string\n", stderr);
                exit(EXIT_FAILURE);
            }
        }
        else if(match(argv[option_index], "--os") || match(argv[option_index], "--OS"))
        {
            if(override) fputs(override_string, stdout);
            else fputs(unameData->sysname, stdout);
            fputc('\n', stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "--blood"))
        {
            if(override) fputs(override_string, stdout);
            else if(match("aarch64", unameData->machine)
                 || match("amd64", unameData->machine)
                 || match("ppc64le", unameData->machine)
                 || match("riscv64", unameData->machine)
                 || match("x86_64", unameData->machine)) fputs("--64", stdout);
            fputc('\n', stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "--endian"))
        {
            if(override) fputs(override_string, stdout);
            else if(match("aarch64", unameData->machine)
                 || match("amd64", unameData->machine)
                 || match("ppc64le", unameData->machine)
                 || match("riscv64", unameData->machine)
                 || match("x86_64", unameData->machine)
                 || match("i386", unameData->machine)
                 || match("i486", unameData->machine)
                 || match("i586", unameData->machine)
                 || match("i686", unameData->machine)
                 || match("i686-pae", unameData->machine))fputs("--little-endian", stdout);
            else fputs("--big-endian", stdout);
            fputc('\n', stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "--hex2"))
        {
            if(override) fputs(override_string, stdout);
            else if(match("aarch64", unameData->machine)) fputs("0x400000", stdout);
            else if(match("armv7l", unameData->machine)) fputs("0x10000", stdout);
            else if(match("amd64", unameData->machine)
                 || match("x86_64", unameData->machine)) fputs("0x600000", stdout);
            else if(match("ppc64le", unameData->machine)) fputs("0x10000", stdout);
            else if(match("riscv64", unameData->machine)) fputs("0x600000", stdout);
            else if(match("i386", unameData->machine)
                 || match("i486", unameData->machine)
                 || match("i586", unameData->machine)
                 || match("i686", unameData->machine)
                 || match("i686-pae", unameData->machine)) fputs("0x08048000", stdout);
            else fputs("0x0", stdout);
            fputc('\n', stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "-V") || match(argv[option_index], "--version"))
        {
            fputs("get_machine 1.5.0\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "-h") || match(argv[option_index], "--help"))
        {
            fputs("If you want exact architecture use --exact\n", stderr);
            fputs("If you want to know the Operating system use --os\n", stderr);
            fputs("If you wish to override the output to anything you want use --override\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    if(override) fputs(override_string, stdout);
    else if(!exact)
    {
        if(match("i386", unameData->machine) ||
           match("i486", unameData->machine) ||
           match("i586", unameData->machine) ||
           match("i686", unameData->machine) ||
           match("i686-pae", unameData->machine)) fputs("x86", stdout);
        else if(match("x86_64", unameData->machine)) fputs("amd64", stdout);
        else fputs(unameData->machine, stdout);
    }
    else fputs(unameData->machine, stdout);
    fputs("\n", stdout);
    return EXIT_SUCCESS;
}

File /mescc-tools-extra/mescc-tools-extra.kaem

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools-extra/mescc-tools-extra.kaem'.
URL: https://github.com/oriansj/mescc-tools-extra/blob/460648be66c4b4e4eeb138b8b88d5d6e0077e79e/mescc-tools-extra.kaem
#!/usr/bin/env bash
## Copyright (C) 2017 Jeremiah Orians
## This file is part of mescc-tools.
##
## mescc-tools is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## mescc-tools is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.

## You need to set the following environmental variables to build the programs:
## ARCH="${ARCH:-x86}"
## M2LIBC="${M2libc:-./M2libc}"
## TOOLS="${TOOLS:-../bin}"
## BINDIR="${BINDIR:-../bin}"

set -ex

M2LIBC_PATH=${M2LIBC}
PATH=${BINDIR}

alias CC="${TOOLS}/M2-Mesoplanet${EXE_SUFFIX} --operating-system ${OPERATING_SYSTEM} --architecture ${ARCH} -f"

CC sha256sum.c -o ${BINDIR}/sha256sum${EXE_SUFFIX}
CC match.c -o ${BINDIR}/match${EXE_SUFFIX}
CC mkdir.c -o ${BINDIR}/mkdir${EXE_SUFFIX}
CC untar.c -o ${BINDIR}/untar${EXE_SUFFIX}
CC ungz.c -o ${BINDIR}/ungz${EXE_SUFFIX}
CC unbz2.c -o ${BINDIR}/unbz2${EXE_SUFFIX}
CC unxz.c -o ${BINDIR}/unxz${EXE_SUFFIX}
CC catm.c -o ${BINDIR}/catm${EXE_SUFFIX}
CC cp.c -o ${BINDIR}/cp${EXE_SUFFIX}
CC chmod.c -o ${BINDIR}/chmod${EXE_SUFFIX}
CC rm.c -o ${BINDIR}/rm${EXE_SUFFIX}
CC replace.c -o ${BINDIR}/replace${EXE_SUFFIX}
CC wrap.c -o ${BINDIR}/wrap${EXE_SUFFIX}

File /mescc-tools-extra/sha256sum.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools-extra/sha256sum.c'.
URL: https://github.com/oriansj/mescc-tools-extra/blob/460648be66c4b4e4eeb138b8b88d5d6e0077e79e/sha256sum.c
/* Copyright (C) 2021 Bastian Bittorf <bb@npl.de>
 * Copyright (C) 2021 Alain Mosnier <alain@wanamoon.net>
 * Copyright (C) 2017-2021 Jan Venekamp
 * Copyright (C) 2021 Jeremiah Orians
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "M2libc/bootstrappable.h"

#define CHUNK_SIZE 64
#define TOTAL_LEN_LEN 8

int mask;

/*
 * Initialize array of round constants:
 * (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
 */
unsigned* init_k()
{
    unsigned* k = calloc(65, sizeof(unsigned));
    k[0] = 0x428a2f98;
    k[1] = 0x71374491;
    k[2] = 0xb5c0fbcf;
    k[3] = 0xe9b5dba5;
    k[4] = 0x3956c25b;
    k[5] = 0x59f111f1;
    k[6] = 0x923f82a4;
    k[7] = 0xab1c5ed5;
    k[8] = 0xd807aa98;
    k[9] = 0x12835b01;
    k[10] = 0x243185be;
    k[11] = 0x550c7dc3;
    k[12] = 0x72be5d74;
    k[13] = 0x80deb1fe;
    k[14] = 0x9bdc06a7;
    k[15] = 0xc19bf174;
    k[16] = 0xe49b69c1;
    k[17] = 0xefbe4786;
    k[18] = 0x0fc19dc6;
    k[19] = 0x240ca1cc;
    k[20] = 0x2de92c6f;
    k[21] = 0x4a7484aa;
    k[22] = 0x5cb0a9dc;
    k[23] = 0x76f988da;
    k[24] = 0x983e5152;
    k[25] = 0xa831c66d;
    k[26] = 0xb00327c8;
    k[27] = 0xbf597fc7;
    k[28] = 0xc6e00bf3;
    k[29] = 0xd5a79147;
    k[30] = 0x06ca6351;
    k[31] = 0x14292967;
    k[32] = 0x27b70a85;
    k[33] = 0x2e1b2138;
    k[34] = 0x4d2c6dfc;
    k[35] = 0x53380d13;
    k[36] = 0x650a7354;
    k[37] = 0x766a0abb;
    k[38] = 0x81c2c92e;
    k[39] = 0x92722c85;
    k[40] = 0xa2bfe8a1;
    k[41] = 0xa81a664b;
    k[42] = 0xc24b8b70;
    k[43] = 0xc76c51a3;
    k[44] = 0xd192e819;
    k[45] = 0xd6990624;
    k[46] = 0xf40e3585;
    k[47] = 0x106aa070;
    k[48] = 0x19a4c116;
    k[49] = 0x1e376c08;
    k[50] = 0x2748774c;
    k[51] = 0x34b0bcb5;
    k[52] = 0x391c0cb3;
    k[53] = 0x4ed8aa4a;
    k[54] = 0x5b9cca4f;
    k[55] = 0x682e6ff3;
    k[56] = 0x748f82ee;
    k[57] = 0x78a5636f;
    k[58] = 0x84c87814;
    k[59] = 0x8cc70208;
    k[60] = 0x90befffa;
    k[61] = 0xa4506ceb;
    k[62] = 0xbef9a3f7;
    k[63] = 0xc67178f2;
    return k;
}

unsigned* init_h()
{
    unsigned* h = calloc(9, sizeof(unsigned));
    h[0] = 0x6a09e667;
    h[1] = 0xbb67ae85;
    h[2] = 0x3c6ef372;
    h[3] = 0xa54ff53a;
    h[4] = 0x510e527f;
    h[5] = 0x9b05688c;
    h[6] = 0x1f83d9ab;
    h[7] = 0x5be0cd19;
    return h;
}

struct buffer_state
{
    char* p;
    size_t len;
    size_t total_len;
    int single_one_delivered; /* bool */
    int total_len_delivered; /* bool */
};

unsigned right_rot(unsigned value, unsigned count)
{
    /*
     * Defined behaviour in standard C for all count where 0 < count < 32,
     * which is what we need here.
     */

    value &= mask;
    int hold1 = (value >> count) & mask;
    int hold2 = (value << (32 - count)) & mask;
    int hold = (hold1 | hold2) & mask;
    return hold;
}

void init_buf_state(struct buffer_state * state, char* input, size_t len)
{
    state->p = input;
    state->len = len;
    state->total_len = len;
    state->single_one_delivered = 0;
    state->total_len_delivered = 0;
}

/* Return value: bool */
int calc_chunk(char* chunk, struct buffer_state * state)
{
    size_t space_in_chunk;

    if(state->total_len_delivered)
    {
        return 0;
    }

    if(state->len >= CHUNK_SIZE)
    {
        memcpy(chunk, state->p, CHUNK_SIZE);
        state->p += CHUNK_SIZE;
        state->len -= CHUNK_SIZE;
        return 1;
    }

    memcpy(chunk, state->p, state->len);
    chunk += state->len;
    space_in_chunk = CHUNK_SIZE - state->len;
    state->p += state->len;
    state->len = 0;

    /* If we are here, space_in_chunk is one at minimum. */
    if(!state->single_one_delivered)
    {
        chunk[0] = 0x80;
        chunk += 1;
        space_in_chunk -= 1;
        state->single_one_delivered = 1;
    }

    /*
     * Now:
     * - either there is enough space left for the total length, and we can conclude,
     * - or there is too little space left, and we have to pad the rest of this chunk with zeroes.
     * In the latter case, we will conclude at the next invocation of this function.
     */
    if(space_in_chunk >= TOTAL_LEN_LEN)
    {
        size_t left = space_in_chunk - TOTAL_LEN_LEN;
        size_t len = state->total_len;
        int i;
        memset(chunk, 0x00, left);
        chunk += left;
        /* Storing of len * 8 as a big endian 64-bit without overflow. */
        chunk[7] = (len << 3);
        len >>= 5;

        for(i = 6; i >= 0; i -= 1)
        {
            chunk[i] = len;
            len >>= 8;
        }

        state->total_len_delivered = 1;
    }
    else
    {
        memset(chunk, 0x00, space_in_chunk);
    }

    return 1;
}

/*
 * Limitations:
 * - Since input is a pointer in RAM, the data to hash should be in RAM, which could be a problem
 *   for large data sizes.
 * - SHA algorithms theoretically operate on bit strings. However, this implementation has no support
 *   for bit string lengths that are not multiples of eight, and it really operates on arrays of bytes.
 *   In particular, the len parameter is a number of bytes.
 */
void calc_sha_256(char* hash, char* input, size_t len)
{
    /*
     * Note 1: All integers (expect indexes) are 32-bit unsigned integers and addition is calculated modulo 2^32.
     * Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 = i = 63
     * Note 3: The compression function uses 8 working variables, a through h
     * Note 4: Big-endian convention is used when expressing the constants in this pseudocode,
     *     and when parsing message block data from bytes to words, for example,
     *     the first word of the input message "abc" after padding is 0x61626380
     */
    /*
     * Initialize hash values:
     * (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
     */
    unsigned* k = init_k();
    unsigned* h = init_h();
    unsigned i;
    unsigned j;
    unsigned hold1;
    unsigned hold2;
    /* 512-bit chunks is what we will operate on. */
    char* chunk = calloc(65, sizeof(char));
    struct buffer_state* state = calloc(1, sizeof(struct buffer_state));
    init_buf_state(state, input, len);
    unsigned* ah = calloc(9, sizeof(unsigned));
    char *p;
    unsigned* w = calloc(17, sizeof(unsigned));
    unsigned s0;
    unsigned s1;
    unsigned ch;
    unsigned temp1;
    unsigned temp2;
    unsigned maj;

    while(calc_chunk(chunk, state))
    {
        p = chunk;

        /* Initialize working variables to current hash value: */
        for(i = 0; i < 8; i += 1)
        {
            ah[i] = h[i];
        }

        /* Compression function main loop: */
        for(i = 0; i < 4; i += 1)
        {
            /*
             * The w-array is really w[64], but since we only need
             * 16 of them at a time, we save stack by calculating
             * 16 at a time.
             *
             * This optimization was not there initially and the
             * rest of the comments about w[64] are kept in their
             * initial state.
             */
            /*
             * create a 64-entry message schedule array w[0..63] of 32-bit words
             * (The initial values in w[0..63] don't matter, so many implementations zero them here)
             * copy chunk into first 16 words w[0..15] of the message schedule array
             */

            for(j = 0; j < 16; j += 1)
            {
                if(i == 0)
                {
                    w[j] = ((p[0] & 0xFF) << 24) | ((p[1] & 0xFF) << 16) | ((p[2] & 0xFF) << 8) | (p[3] & 0xFF);
                    p += 4;
                }
                else
                {
                    /* Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array: */
                    hold1 = (j + 1) & 0xf;
                    hold2 = w[hold1];
                    s0 = right_rot(hold2, 7) ^ right_rot(hold2, 18) ^ ((hold2 & mask) >> 3);

                    hold1 = (j + 14) & 0xf;
                    hold2 = w[hold1];
                    s1 = right_rot(hold2, 17) ^ right_rot(hold2, 19) ^ ((hold2 & mask) >> 10);

                    w[j] += s0 + w[(j + 9) & 0xf] + s1;
                }

                s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25);
                ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]);
                temp1 = ah[7] + s1 + ch + k[i << 4 | j] + w[j];
                s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22);
                maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]);
                temp2 = s0 + maj;
                ah[7] = ah[6];
                ah[6] = ah[5];
                ah[5] = ah[4];
                ah[4] = ah[3] + temp1;
                ah[3] = ah[2];
                ah[2] = ah[1];
                ah[1] = ah[0];
                ah[0] = temp1 + temp2;
            }
        }

        /* Add the compressed chunk to the current hash value: */
        for(i = 0; i < 8; i +=  1)
        {
            h[i] += ah[i];
        }
    }

    /* Produce the final hash value (big-endian): */
    i = 0;
    for(j = 0; i < 8; i += 1)
    {
        hash[j] = ((h[i] >> 24) & 0xFF);
        j += 1;
        hash[j] = ((h[i] >> 16) & 0xFF);
        j += 1;
        hash[j] = ((h[i] >> 8) & 0xFF);
        j += 1;
        hash[j] = (h[i] & 0xFF);
        j += 1;
    }
}

struct list
{
    int found;
    char* name;
    FILE* f;
    size_t size;
    char* buffer;
    char* hash;
    struct list* next;
};

void bad_checkfile(char* filename)
{
    fputs(filename, stdout);
    puts(": no properly formatted SHA256 checksum lines found");
}

int hex2int(char c, char* filename)
{
    if((c >= '0') && (c <= '9')) return (c - 48);
    else if((c >= 'a') && (c <= 'f')) return (c - 87);
    else if ((c >= 'F') && (c <= 'F')) return (c - 55);
    bad_checkfile(filename);
    exit(EXIT_FAILURE);
}

char* hash_to_string(char* a)
{
    char* table = "0123456789abcdef";
    char* r = calloc(66, sizeof(char));
    int i;
    int j = 0;
    int c;
    for(i = 0; i < 32; i += 1)
    {
        c = a[i] & 0xFF;
        r[j] = table[(c >> 4)];
        j += 1;
        r[j] = table[(c & 0xF)];
        j += 1;
    }
    return r;
}

int check_file(char* b, char* filename)
{
    int r = TRUE;
    size_t i;
    int hold1;
    int hold2;
    FILE* f;
    char* name = calloc(4097, sizeof(char));
    char* hash = calloc(33, sizeof(char));
    char* hash2 = calloc(33, sizeof(char));
    size_t size;
    char* buffer;
go_again:
    for(i = 0; i < 32; i += 1)
    {
        hold1 = hex2int(b[0], filename);
        hold2 = hex2int(b[1], filename);
        hash[i] = (hold1 << 4) + hold2;
        b += 2;
    }

    if((' ' != b[0]) || (' ' != b[1]))
    {
        bad_checkfile(filename);
        exit(EXIT_FAILURE);
    }

    b += 2;
    for(i = 0; i < 4096; i += 1)
    {
        if('\n' == b[0])
        {
            name[i] = 0;
            b += 1;
            break;
        }
        name[i] = b[0];
        b += 1;
    }

    f = fopen(name, "r");
    if(NULL == f)
    {
        fputs(name, stdout);
        puts(": No such file or directory");
        exit(EXIT_FAILURE);
    }
    else
    {
        fseek(f, 0, SEEK_END);
        size = ftell(f);
        rewind(f);
        buffer = calloc(size + 1, sizeof(char));
        fread(buffer, sizeof(char), size, f);
        calc_sha_256(hash2, buffer, size);
        if(match(hash_to_string(hash), hash_to_string(hash2)))
        {
            fputs(name, stdout);
            puts(": OK");
        }
        else
        {
            fputs(name, stdout);
            fputs(": FAILED\nWanted:   ", stdout);
            fputs(hash_to_string(hash), stdout);
            fputs("\nReceived: ", stdout);
            puts(hash_to_string(hash2));
            r = FALSE;
        }
    }

    if(0 == b[0]) return r;
    goto go_again;
}

/* reverse the linked list */
void reverse(struct list** head)
{
    struct list* prev = NULL;
    struct list* current = *head;
    struct list* next = NULL;
    while (current != NULL)
    {
        next = current->next;
        current->next = prev;
        prev = current;
        current = next;
    }
    *head = prev;
}

int main(int argc, char **argv)
{
    struct list* l = NULL;
    struct list* t = NULL;
    size_t read;
    int check = FALSE;
    int r = TRUE;
    char* output_file = "";
    FILE* output = stdout;
    mask = (0x7FFFFFFF << 1) | 0x1;

    int i = 1;
    while(i <= argc)
    {
        if(NULL == argv[i])
        {
            i += 1;
        }
        else if(match(argv[i], "-c") || match(argv[i], "--check"))
        {
            check = TRUE;
            i += 1;
        }
        else if (match(argv[i], "-o") || match(argv[i], "--output"))
        {
            output_file = argv[i + 1];
            i += 2;
            if (output != stdout) {
                fclose(output);
            }
            output = fopen(output_file, "w");
            require(output != NULL, "Output file cannot be opened!\n");
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            puts("Usage: sha256sum <file> [--check]");
            exit(EXIT_SUCCESS);
        }
        else
        {
            t = calloc(1, sizeof(struct list));
            t->hash = calloc(33, sizeof(char));
            t->name = argv[i];
            t->f = fopen(t->name, "r");
            if(NULL != t->f)
            {
                t->found = TRUE;
                fseek(t->f, 0, SEEK_END);
                t->size = ftell(t->f);
                rewind(t->f);
                t->buffer = calloc(t->size + 1, sizeof(char));
                read = fread(t->buffer, sizeof(char), t->size, t->f);
            }
            t->next = l;
            l = t;
            i += 1;
        }
    }
    reverse(&l);

    if(check)
    {
        while(NULL != l)
        {
            if(l->found)
            {
                if(!check_file(l->buffer, l->name)) r = FALSE;
            }
            else
            {
                fputs(l->name, stdout);
                puts(": No such file or directory");
                exit(EXIT_FAILURE);
            }
            l = l->next;
        }
    }
    else
    {
        while(NULL != l)
        {
            if(l->found)
            {
                calc_sha_256(l->hash, l->buffer, l->size);
                fputs(hash_to_string(l->hash), output);
                fputs("  ", output);
                fputs(l->name, output);
                fputc('\n', output);
            }
            else
            {
                fputs(l->name, output);
                fputs(": No such file or directory\n", output);
                exit(EXIT_FAILURE);
            }
            l = l->next;
        }
    }
    if (output != stdout) {
        fclose(output);
    }

    if(r) return 0;
    else return 1;
}

File /M2libc/string.h

Live-bootstrap source file is 'seed/stage0-posix/M2libc/string.h'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/string.h
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _STRING_H
#define _STRING_H

#ifdef __M2__
#include <string.c>
#else
#include <stddef.h>

/* String manipulation */
char* strcpy(char* dest, char const* src);
char* strncpy(char* dest, char const* src, size_t count);
char* strcat(char* dest, char const* src);
char* strncat(char* dest, char const* src, size_t count);

/* String examination */
size_t strlen(char const* str );
size_t strnlen_s(char const* str, size_t strsz );
int strcmp(char const* lhs, char const* rhs );
int strncmp(char const* lhs, char const* rhs, size_t count);
char* strchr(char const* str, int ch);
char* strrchr(char const* str, int ch);
size_t strspn(char const* dest, char const* src);
size_t strcspn(char const* dest, char const* src);
char* strpbrk(char const* dest, char const* breakset);

/* Memory manipulation */
void* memset(void* dest, int ch, size_t count);
void* memcpy(void* dest, void const* src, size_t count);
void* memmove(void* dest, void const* src, size_t count);
int memcmp(void const* lhs, void const* rhs, size_t count);
void* memchr(void const* ptr, int ch, size_t count);
char* strstr(const char* haystack, const char* needle);
#endif
#endif

File /M2libc/sys/stat.h

Live-bootstrap source file is 'seed/stage0-posix/M2libc/sys/stat.h'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/sys/stat.h
/* Copyright (C) 2020 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SYS_STAT_H
#define _SYS_STAT_H

#ifdef __M2__
#if __uefi__
#include <uefi/sys/stat.c>
#elif __i386__
#include <x86/linux/sys/stat.c>
#elif __x86_64__
#include <amd64/linux/sys/stat.c>
#elif __arm__
#include <armv7l/linux/sys/stat.c>
#elif __aarch64__
#include <aarch64/linux/sys/stat.c>
#elif __riscv && __riscv_xlen==32
#include <riscv32/linux/sys/stat.c>
#elif __riscv && __riscv_xlen==64
#include <riscv64/linux/sys/stat.c>
#else
#error arch not supported
#endif

#else
#include <sys/types.h>

#define S_IRWXU 00700
#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400

#define S_ISUID 04000
#define S_ISGID 02000
#define S_IXGRP 00010
#define S_IXOTH 00001
#define S_IRGRP 00040
#define S_IROTH 00004
#define S_IWGRP 00020
#define S_IWOTH 00002
#define S_IRWXG 00070
#define S_IRWXO 00007


int chmod(char *pathname, int mode);
int fchmod(int a, mode_t b);
int mkdir(char const* a, mode_t b);
int mknod(char const* a, mode_t b, dev_t c);
mode_t umask(mode_t m);

#endif
#endif

File /M2libc/uefi/sys/stat.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/uefi/sys/stat.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/uefi/sys/stat.c
/* Copyright (C) 2020 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SYS_STAT_C
#define _SYS_STAT_C

#include <uefi/uefi.c>
#include <sys/types.h>

#define S_IRWXU 00700
#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400

#define S_ISUID 04000
#define S_ISGID 02000
#define S_IXGRP 00010
#define S_IXOTH 00001
#define S_IRGRP 00040
#define S_IROTH 00004
#define S_IWGRP 00020
#define S_IWOTH 00002
#define S_IRWXG 00070
#define S_IRWXO 00007


int chmod(char *pathname, int mode)
{
    return 0;
}


int fchmod(int a, mode_t b)
{
    return 0;
}

int __open(struct efi_file_protocol* _rootdir, char* name, long mode, long attributes);
int mkdir(char const* name, mode_t _mode)
{
    struct efi_file_protocol* new_directory;
    long mode = EFI_FILE_MODE_CREATE | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ;
    long attributes = EFI_FILE_DIRECTORY;
    long new_directory = __open(_rootdir, name, mode, attributes);
    if(new_directory != -1)
    {
        _close(new_directory);
        return 0;
    }
    return -1;
}


int mknod(char const* a, mode_t b, dev_t c)
{
    return -1;
}


mode_t umask(mode_t m)
{
    return 0;
}

#endif

File /M2libc/uefi/uefi.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/uefi/uefi.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/uefi/uefi.c
/* Copyright (C) 2022 Andrius Å tikonas
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#define __uefi__ 1

#ifndef _UEFI_C
#define _UEFI_C

#include <ctype.h>
#include <uefi/string_p.h>

#define PAGE_SIZE 4096
#define USER_STACK_SIZE 8388608
#define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 1
#define EFI_FILE_MODE_READ 1
#define EFI_FILE_MODE_WRITE 2
#define EFI_FILE_MODE_CREATE (1 << 63)
#define EFI_FILE_READ_ONLY 1
#define EFI_FILE_DIRECTORY 0x10
#define EFI_LOADER_DATA 2

#define EFI_VARIABLE_BOOTSERVICE_ACCESS 2

#define EFI_SUCCESS 0
#define EFI_LOAD_ERROR (1 << 63) | 1
#define EFI_INVALID_PARAMETER (1 << 63) | 2
#define EFI_UNSUPPORTED (1 << 63) | 3
#define EFI_BUFFER_TOO_SMALL (1 << 63) | 5
#define EFI_NOT_FOUND (1 << 31) | 14

#define __PATH_MAX 4096
#define __ENV_NAME_MAX 4096

#define HARDWARE_DEVICE_PATH 1
#define MEMORY_MAPPED 3
#define END_HARDWARE_DEVICE_PATH 0x7F
#define END_ENTIRE_DEVICE_PATH 0xFF

#define TPL_APPLICATION    4
#define TPL_CALLBACK       8
#define TPL_NOTIFY         16
#define TPL_HIGH_LEVEL     31

void* _image_handle;
void* _root_device;
void* __user_stack;

int _argc;
char** _argv;
char** _envp;

char* _cwd;
char* _root;

struct efi_simple_text_output_protocol
{
    void* reset;
    void* output_string;
    void* test_string;
    void* query_mode;
    void* set_mode;
    void* set_attribute;
    void* clear_screen;
    void* set_cursor;
    void* enable_cursor;
    void* mode;
};

struct efi_table_header
{
    unsigned signature;
    unsigned revision_and_header_size;
    unsigned crc32_and_reserved;
};

struct efi_boot_table
{
    struct efi_table_header header;

    /* Task Priority Services */
    void* raise_tpl;
    void* restore_tpl;

    /* Memory Services */
    void* allocate_pages;
    void* free_pages;
    void* get_memory_map;
    void* allocate_pool;
    void* free_pool;

    /* Event & Timer Services */
    void* create_event;
    void* set_timer;
    void* wait_for_event;
    void* signal_event;
    void* close_event;
    void* check_event;

    /* Protocol Handler Services */
    void* install_protocol_interface;
    void* reinstall_protocol_interface;
    void* uninstall_protocol_interface;
    void* handle_protocol;
    void* reserved;
    void* register_protocol_notify;
    void* locate_handle;
    void* locate_device_path;
    void* install_configuration_table;

    /* Image Services */
    void* load_image;
    void* start_image;
    void* exit;
    void* unload_image;
    void* exit_boot_services;

    /* Miscellaneous Services */
    void* get_next_monotonic_count;
    void* stall;
    void* set_watchdog_timer;

    /* DriverSupport Services */
    void* connect_controller;
    void* disconnect_controller;

    /* Open and Close Protocol Services */
    void* open_protocol;
    void* close_protocol;
    void* open_protocol_information;

    /* Library Services */
    void* protocols_per_handle;
    void* locate_handle_buffer;
    void* locate_protocol;
    void* install_multiple_protocol_interfaces;
    void* uninstall_multiple_protocol_interfaces;

    /* 32-bit CRC Services */
    void* copy_mem;
    void* set_mem;
    void* create_event_ex;
};

struct efi_runtime_table
{
    struct efi_table_header header;

    /* Time Services */
    void* get_time;
    void* set_time;
    void* get_wakeup_time;
    void* set_wakeup_time;

    /* Virtual Memory Services */
    void* set_virtual_address_map;
    void* convert_pointer;

    /* Variable Services */
    void* get_variable;
    void* get_next_variable_name;
    void* set_variable;

    /* Miscellaneous Services */
    void* get_next_high_monotonic_count;
    void* reset_system;

    /* UEFI 2.0 Capsule Services */
    void* update_capsule;
    void* query_capsule_capabilities;

    /* Miscellaneous UEFI 2.0 Services */
    void* query_variable_info;
};

struct efi_system_table
{
    struct efi_table_header header;

    char* firmware_vendor;
    unsigned firmware_revision;
    void* console_in_handle;
    void* con_in;
    void* console_out_handle;
    struct efi_simple_text_output_protocol* con_out;
    void *standard_error_handle;
    struct efi_simple_text_output_protocol* std_err;
    struct efi_runtime_table* runtime_services;
    struct efi_boot_table* boot_services;
    unsigned number_table_entries;
    void *configuration_table;
};
struct efi_system_table* _system;

struct efi_guid
{
    uint32_t data1;
    uint16_t data2;
    uint16_t data3;
    uint8_t data4[8];
};
struct efi_guid EFI_LOADED_IMAGE_PROTOCOL_GUID;
struct efi_guid EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
struct efi_guid EFI_FILE_INFO_GUID;
struct efi_guid EFI_SHELL_VARIABLE_GUID;

struct efi_loaded_image_protocol
{
    unsigned revision;
    void* parent;
    void* system;

    void* device;
    void* filepath;
    void* reserved;

    /* Image's load options */
    unsigned load_options_size;
    void* load_options;

    /* Location of the image in memory */
    void* image_base;
    unsigned image_size;
    unsigned image_code_type;
    unsigned image_data_type;
    void* unload;
};

struct efi_loaded_image_protocol* _image;

struct efi_simple_file_system_protocol
{
    unsigned revision;
    void* open_volume;
};

struct efi_file_protocol
{
    unsigned revision;
    void* open;
    void* close;
    void* delete;
    void* read;
    void* write;
    void* get_position;
    void* set_position;
    void* get_info;
    void* set_info;
    void* flush;
    void* open_ex;
    void* read_ex;
    void* write_ex;
    void* flush_ex;
};
struct efi_file_protocol* _rootdir;

struct efi_time
{
    uint16_t year;
    uint8_t month;
    uint8_t day;
    uint8_t hour;
    uint8_t minute;
    uint8_t second;
    uint8_t pad1;
    uint32_t nanosecond;
    uint16_t time_zone;
    uint8_t daylight;
    uint8_t pad2;
};

struct efi_file_info
{
    unsigned size;
    unsigned file_size;
    unsigned physical_size;
    struct efi_time create_time;
    struct efi_time last_access_time;
    struct efi_time modifiction_time;
    unsigned attribute;
    char file_name[__PATH_MAX];
};

struct efi_device_path_protocol
{
    uint8_t type;
    uint8_t subtype;
    uint16_t length;
    uint32_t memory_type;
    unsigned start_address;
    unsigned end_address;
};

unsigned __uefi_1(void*, void*, FUNCTION f)
{
#ifdef __x86_64__
    asm("lea_rcx,[rbp+DWORD] %-8"
        "mov_rcx,[rcx]"
        "lea_rax,[rbp+DWORD] %-16"
        "mov_rax,[rax]"
        "push_rsp"
        "push_[rsp]"
        "and_rsp, %-16"
        "sub_rsp, %32"
        "call_rax"
        "mov_rsp,[rsp+BYTE] %40");
#else
#error unsupported arch
#endif
}

unsigned __uefi_2(void*, void*, FUNCTION f)
{
#ifdef __x86_64__
    asm("lea_rcx,[rbp+DWORD] %-8"
        "mov_rcx,[rcx]"
        "lea_rdx,[rbp+DWORD] %-16"
        "mov_rdx,[rdx]"
        "lea_rax,[rbp+DWORD] %-24"
        "mov_rax,[rax]"
        "push_rsp"
        "push_[rsp]"
        "and_rsp, %-16"
        "sub_rsp, %32"
        "call_rax"
        "mov_rsp,[rsp+BYTE] %40");
#else
#error unsupported arch
#endif
}

unsigned __uefi_3(void*, void*, void*, FUNCTION f)
{
#ifdef __x86_64__
    asm("lea_rcx,[rbp+DWORD] %-8"
        "mov_rcx,[rcx]"
        "lea_rdx,[rbp+DWORD] %-16"
        "mov_rdx,[rdx]"
        "lea_r8,[rbp+DWORD] %-24"
        "mov_r8,[r8]"
        "lea_rax,[rbp+DWORD] %-32"
        "mov_rax,[rax]"
        "push_rsp"
        "push_[rsp]"
        "and_rsp, %-16"
        "sub_rsp, %32"
        "call_rax"
        "mov_rsp,[rsp+BYTE] %40");
#else
#error unsupported arch
#endif
}

unsigned __uefi_4(void*, void*, void*, void*, FUNCTION f)
{
#ifdef __x86_64__
    asm("lea_rcx,[rbp+DWORD] %-8"
        "mov_rcx,[rcx]"
        "lea_rdx,[rbp+DWORD] %-16"
        "mov_rdx,[rdx]"
        "lea_r8,[rbp+DWORD] %-24"
        "mov_r8,[r8]"
        "lea_r9,[rbp+DWORD] %-32"
        "mov_r9,[r9]"
        "lea_rax,[rbp+DWORD] %-40"
        "mov_rax,[rax]"
        "push_rsp"
        "push_[rsp]"
        "and_rsp, %-16"
        "sub_rsp, %32"
        "call_rax"
        "mov_rsp,[rsp+BYTE] %40");
#else
#error unsupported arch
#endif
}

unsigned __uefi_5(void*, void*, void*, void*, void*, FUNCTION f)
{
#ifdef __x86_64__
    asm("lea_rcx,[rbp+DWORD] %-8"
        "mov_rcx,[rcx]"
        "lea_rdx,[rbp+DWORD] %-16"
        "mov_rdx,[rdx]"
        "lea_r8,[rbp+DWORD] %-24"
        "mov_r8,[r8]"
        "lea_r9,[rbp+DWORD] %-32"
        "mov_r9,[r9]"
        "push_rsp"
        "push_[rsp]"
        "and_rsp, %-16"
        "push_rax"
        "lea_rax,[rbp+DWORD] %-40"
        "mov_rax,[rax]"
        "push_rax"
        "lea_rax,[rbp+DWORD] %-48"
        "mov_rax,[rax]"
        "sub_rsp, %32"
        "call_rax"
        "mov_rsp,[rsp+BYTE] %56");
#else
#error unsupported arch
#endif
}

unsigned __uefi_6(void*, void*, void*, void*, void*, void*, FUNCTION f)
{
#ifdef __x86_64__
    asm("lea_rcx,[rbp+DWORD] %-8"
        "mov_rcx,[rcx]"
        "lea_rdx,[rbp+DWORD] %-16"
        "mov_rdx,[rdx]"
        "lea_r8,[rbp+DWORD] %-24"
        "mov_r8,[r8]"
        "lea_r9,[rbp+DWORD] %-32"
        "mov_r9,[r9]"
        "push_rsp"
        "push_[rsp]"
        "and_rsp, %-16"
        "lea_rax,[rbp+DWORD] %-48"
        "mov_rax,[rax]"
        "push_rax"
        "lea_rax,[rbp+DWORD] %-40"
        "mov_rax,[rax]"
        "push_rax"
        "lea_rax,[rbp+DWORD] %-56"
        "mov_rax,[rax]"
        "sub_rsp, %32"
        "call_rax"
        "mov_rsp,[rsp+BYTE] %56");
#else
#error unsupported arch
#endif
}

unsigned _allocate_pool(unsigned memory_type, unsigned size, void* pool)
{
    return __uefi_3(memory_type, size, pool, _system->boot_services->allocate_pool);
}

void _free_pool(void* memory)
{
    return __uefi_1(memory, _system->boot_services->free_pool);
}

unsigned _open_protocol(void* handle, struct efi_guid* protocol, void* agent_handle, void** interface, void* controller_handle, long attributes, FUNCTION open_protocol)
{
    return __uefi_6(handle, protocol, agent_handle, interface, controller_handle, attributes, _system->boot_services->open_protocol);
}

unsigned _close_protocol(void* handle, struct efi_guid* protocol, void* agent_handle, void* controller_handle)
{
    return __uefi_4(handle, protocol, agent_handle, controller_handle, _system->boot_services->close_protocol);
}

unsigned _open_volume(struct efi_simple_file_system_protocol* rootfs, struct efi_file_protocol** rootdir)
{
    return __uefi_2(rootfs, rootdir, rootfs->open_volume);
}

unsigned _close(struct efi_file_protocol* file)
{
    return __uefi_1(file, file->close);
}

unsigned _get_next_variable_name(unsigned* size, char* name, struct efi_guid* vendor_guid)
{
    return __uefi_3(size, name, vendor_guid, _system->runtime_services->get_next_variable_name);
}

unsigned _get_variable(char* name, struct efi_guid* vendor_guid, uint32_t* attributes, unsigned* data_size, void* data)
{
    return __uefi_5(name, vendor_guid, attributes, data_size, data, _system->runtime_services->get_variable);
}

char* _string2wide(char* narrow_string);
size_t strlen(char const* str);
void free(void* ptr);
unsigned _set_variable(char* name, void* data)
{
    char* wide_name = _string2wide(name);
    char* wide_data = _string2wide(data);
    unsigned data_size = strlen(data) * 2;
    uint32_t attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS;
    unsigned rval = __uefi_5(wide_name, &EFI_SHELL_VARIABLE_GUID, attributes, data_size, wide_data, _system->runtime_services->set_variable);
    free(wide_name);
    free(wide_data);
    return rval;
}

void exit(unsigned value)
{
    goto FUNCTION__exit;
}

char* strcat(char* dest, char const* src);
char* strcpy(char* dest, char const* src);
size_t strlen(char const* str);
void* calloc(int count, int size);

char* _relative_path_to_absolute(char* narrow_string)
{
    char* absolute_path = calloc(__PATH_MAX, 1);
    if(narrow_string[0] != '/' && narrow_string[0] != '\\')
    {
        strcat(absolute_path, _cwd);
        if(_cwd[strlen(_cwd) - 1] != '/' && _cwd[strlen(_cwd) - 1] != '\\')
        {
            strcat(absolute_path, "/");
        }
    }
    else
    {
        strcat(absolute_path, _root);
        }
    strcat(absolute_path, narrow_string);

    return absolute_path;
}

char* _posix_path_to_uefi(char* narrow_string)
{
    char* absolute_path = _relative_path_to_absolute(narrow_string);

    unsigned length = strlen(absolute_path);
    unsigned in = 0;
    unsigned out = 0;
    while(in < length)
    {
        if(absolute_path[in] == '/')
        {
            absolute_path[out] = '\\';
            // Deal with /./ in paths.
            if((in < (length - 1)) && (absolute_path[in + 1] == '.') && (absolute_path[in + 2] == '/'))
            {
                in += 2;
            }
        }
        else
        {
            absolute_path[out] = absolute_path[in];
        }
        in += 1;
        out += 1;
    }
    absolute_path[out] = 0;

    char* wide_string = _string2wide(absolute_path);
    free(absolute_path);
    return wide_string;
}

char* _string2wide(char* narrow_string)
{
    unsigned length = strlen(narrow_string);
    char* wide_string = calloc(length + 1, 2);
    unsigned i;
    for(i = 0; i < length; i += 1)
    {
        wide_string[2 * i] = narrow_string[i];
    }
    return wide_string;
}

int isspace(char _c);

void _process_load_options(char* load_options)
{
    /* Determine argc */
    _argc = 1; /* command name */
    char *i = load_options;
    unsigned was_space = 0;
    do
    {
        if(isspace(i[0]))
        {
            if(!was_space)
            {
                _argc += 1;
                was_space = 1;
            }
        }
        else
        {
            was_space = 0;
        }
        i += 1;
    } while(i[0] != 0);

    /* Collect argv */
    _argv = calloc(_argc + 1, sizeof(char*));
    i = load_options;
    unsigned j;
    for(j = 0; j < _argc; j += 1)
    {
        _argv[j] = i;
        do
        {
            i += 1;
        } while(!isspace(i[0]) && i[0] != 0);
        i[0] = 0;
        do
        {
            i += 1;
        } while(isspace(i[0]));
    }
}

/* Function to find the length of a char**; an array of strings */
unsigned _array_length(char** array)
{
    unsigned length = 0;

    while(array[length] != NULL)
    {
        length += 1;
    }

    return length;
}

size_t wcstombs(char* dest, char* src, size_t n);

char* _get_environmental_variable(struct efi_guid* vendor_guid, char* name, unsigned size)
{
    unsigned data_size;
    char* data;
    char* variable_data;
    char* envp_line = NULL;

    /* Call with data=NULL to obtain data size that we need to allocate */
    _get_variable(name, vendor_guid, NULL, &data_size, NULL);
    data = calloc(data_size + 1, 1);
    _get_variable(name, vendor_guid, NULL, &data_size, data);

    variable_data = calloc((data_size / 2) + 1, 1);
    wcstombs(variable_data, data, (data_size / 2) + 1);

    envp_line = calloc((size / 2) + (data_size / 2) + 1, 1);
    wcstombs(envp_line, name, size / 2);
    strcat(envp_line, "=");
    strcat(envp_line, variable_data);
    free(data);
    free(variable_data);

    return envp_line;
}

int memcmp(void const* lhs, void const* rhs, size_t count);

char** _get_environmental_variables(char** envp)
{
    EFI_SHELL_VARIABLE_GUID.data1 = 0x158def5a;
    EFI_SHELL_VARIABLE_GUID.data2 = 0xf656;
    EFI_SHELL_VARIABLE_GUID.data3 = 0x419c;
    EFI_SHELL_VARIABLE_GUID.data4[0] = 0xb0;
    EFI_SHELL_VARIABLE_GUID.data4[1] = 0x27;
    EFI_SHELL_VARIABLE_GUID.data4[2] = 0x7a;
    EFI_SHELL_VARIABLE_GUID.data4[3] = 0x31;
    EFI_SHELL_VARIABLE_GUID.data4[4] = 0x92;
    EFI_SHELL_VARIABLE_GUID.data4[5] = 0xc0;
    EFI_SHELL_VARIABLE_GUID.data4[6] = 0x79;
    EFI_SHELL_VARIABLE_GUID.data4[7] = 0xd2;

    unsigned size = __ENV_NAME_MAX;
    unsigned rval;
    unsigned envc = 0;
    char* name = calloc(size, 1);

    struct efi_guid vendor_guid;
    /* First count the number of environmental variables */
    do
    {
        size = __ENV_NAME_MAX;
        rval = _get_next_variable_name(&size, name, &vendor_guid);
        if(rval == EFI_SUCCESS)
        {
            if(memcmp(&vendor_guid, &EFI_SHELL_VARIABLE_GUID, sizeof(struct efi_guid)) == 0)
            {
                envc += 1;
            }
        }
    } while(rval == EFI_SUCCESS);

    /* Now redo the search but this time populate envp array */
    envp = calloc(sizeof(char*), envc + 1);
    name[0] = 0;
    name[1] = 0;
    unsigned j = 0;
    do
    {
        size = __ENV_NAME_MAX;
        rval = _get_next_variable_name(&size, name, &vendor_guid);
        if(rval == EFI_SUCCESS)
        {
            if(memcmp(&vendor_guid, &EFI_SHELL_VARIABLE_GUID, sizeof(struct efi_guid)) == 0)
            {
                envp[j] = _get_environmental_variable(&vendor_guid, name, size);
                j += 1;
            }
        }
    } while(rval == EFI_SUCCESS);
    envp[j] = 0;
    free(name);

    return envp;
}

void _wipe_environment()
{
    char** envp = _get_environmental_variables(envp);
    unsigned i = 0;
    unsigned j;
    char* name;
    while(envp[i] != 0)
    {
        j = 0;
        name = envp[i];
        while(envp[i][j] != '=')
        {
            j += 1;
        }
        envp[i][j] = 0;
        _set_variable(name, "");
        i += 1;
    }
    free(envp);
}

int strcmp(char const* lhs, char const* rhs);
char* strchr(char const* str, int ch);

void _setup_current_working_directory(char** envp)
{
    _cwd = calloc(__PATH_MAX, 1);
    _root = calloc(__PATH_MAX, 1);

    unsigned i = 0;
    unsigned j;
    unsigned k;
    char* value;
    char* match;

    while(envp[i] != 0)
    {
        j = 0;
        while(envp[i][j] != '=')
        {
            j += 1;
        }
        envp[i][j] = 0;
        if(strcmp(envp[i], "root") == 0)
        {
            value = envp[i] + j + 1;
            match = strchr(value, ':'); /* strip uefi device, e.g. fs0: */
            if(match != NULL)
            {
                value = match + 1;
            }
            strcpy(_root, value);
            k = 0;
            while(_root[k] != '\0')
            {
                if(_root[k] == '\\')
                {
                    _root[k] = '/';
                }
                k += 1;
            }
        }
        else if(strcmp(envp[i], "cwd") == 0)
        {
            value = envp[i] + j + 1;
            match = strchr(value, ':'); /* strip uefi device, e.g. fs0: */
            if(match != NULL)
            {
                value = match + 1;
            }
            strcpy(_cwd, value);
            k = 0;
            while(_cwd[k] != '\0')
            {
                if(_cwd[k] == '\\')
                {
                    _cwd[k] = '/';
                }
                k += 1;
            }
        }
        envp[i][j] = '=';
        i += 1;
    }
    if(strcmp(_cwd, "") == 0)
    {
        strcpy(_cwd, "/");
    }
}

void* malloc(unsigned size);
void __init_io();

void _init()
{
    /* Allocate user stack, UEFI stack is not big enough for compilers */
    __user_stack = malloc(USER_STACK_SIZE) + USER_STACK_SIZE;

    /* Process command line arguments */
    EFI_LOADED_IMAGE_PROTOCOL_GUID.data1 = 0x5b1b31a1;
    EFI_LOADED_IMAGE_PROTOCOL_GUID.data2 = 0x9562;
    EFI_LOADED_IMAGE_PROTOCOL_GUID.data3 = 0x11d2;
    EFI_LOADED_IMAGE_PROTOCOL_GUID.data4[0] = 0x8e;
    EFI_LOADED_IMAGE_PROTOCOL_GUID.data4[1] = 0x3f;
    EFI_LOADED_IMAGE_PROTOCOL_GUID.data4[2] = 0;
    EFI_LOADED_IMAGE_PROTOCOL_GUID.data4[3] = 0xa0;
    EFI_LOADED_IMAGE_PROTOCOL_GUID.data4[4] = 0xc9;
    EFI_LOADED_IMAGE_PROTOCOL_GUID.data4[5] = 0x69;
    EFI_LOADED_IMAGE_PROTOCOL_GUID.data4[6] = 0x72;
    EFI_LOADED_IMAGE_PROTOCOL_GUID.data4[7] = 0x3b;

    __init_io();
    _open_protocol(_image_handle, &EFI_LOADED_IMAGE_PROTOCOL_GUID, &_image, _image_handle, 0, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
    char* load_options = calloc(_image->load_options_size, 1);
    wcstombs(load_options, _image->load_options, _image->load_options_size);
    _process_load_options(load_options);

    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID.data1 = 0x964E5B22;
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID.data2 = 0x6459;
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID.data3 = 0x11d2;
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID.data4[0] = 0x8e;
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID.data4[1] = 0x39;
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID.data4[2] = 0;
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID.data4[3] = 0xa0;
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID.data4[4] = 0xc9;
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID.data4[5] = 0x69;
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID.data4[6] = 0x72;
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID.data4[7] = 0x3b;

    _root_device = _image->device;
    struct efi_simple_file_system_protocol* rootfs;
    _open_protocol(_root_device, &EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, &rootfs, _image_handle, 0, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
    _open_volume(rootfs, &_rootdir);

    EFI_FILE_INFO_GUID.data1 = 0x09576e92;
    EFI_FILE_INFO_GUID.data2 = 0x6d3f;
    EFI_FILE_INFO_GUID.data3 = 0x11d2;
    EFI_FILE_INFO_GUID.data4[0] = 0x8e;
    EFI_FILE_INFO_GUID.data4[1] = 0x39;
    EFI_FILE_INFO_GUID.data4[2] = 0;
    EFI_FILE_INFO_GUID.data4[3] = 0xa0;
    EFI_FILE_INFO_GUID.data4[4] = 0xc9;
    EFI_FILE_INFO_GUID.data4[5] = 0x69;
    EFI_FILE_INFO_GUID.data4[6] = 0x72;
    EFI_FILE_INFO_GUID.data4[7] = 0x3b;

    _envp = _get_environmental_variables(_envp);
    _setup_current_working_directory(_envp);
}

void __kill_io();
void* _malloc_release_all(FUNCTION _free);

void _cleanup()
{
    __kill_io();
    _close(_rootdir);
    _close_protocol(_root_device, &EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, _image_handle, 0);
    _close_protocol(_image_handle, &EFI_LOADED_IMAGE_PROTOCOL_GUID, _image_handle, 0);
    _malloc_release_all(_free_pool);
}

void* _malloc_uefi(unsigned size)
{
    void* memory_block;
    if(_allocate_pool(EFI_LOADER_DATA, size, &memory_block) != EFI_SUCCESS)
    {
        return 0;
    }
    return memory_block;
}

#endif

File /M2libc/ctype.h

Live-bootstrap source file is 'seed/stage0-posix/M2libc/ctype.h'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/ctype.h
/* Copyright (C) 2022 Andrius Å tikonas
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */


#ifndef _CTYPE_H
#define _CTYPE_H

#ifdef __M2__
#include <ctype.c>
#else
#endif

#endif

File /M2libc/ctype.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/ctype.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/ctype.c
/* Copyright (C) 2022 Andrius Å tikonas
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

int isspace(char _c)
{
    return _c == ' ' || _c - '\t' < 5;
}

File /M2libc/uefi/string_p.h

Live-bootstrap source file is 'seed/stage0-posix/M2libc/uefi/string_p.h'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/uefi/string_p.h
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stddef.h>

char* strcpy(char* dest, char const* src)
{
    int i = 0;

    while (0 != src[i])
    {
        dest[i] = src[i];
        i = i + 1;
    }
    dest[i] = 0;

    return dest;
}


char* strncpy(char* dest, char const* src, size_t count)
{
    if(0 == count) return dest;
    size_t i = 0;
    while(0 != src[i])
    {
        dest[i] = src[i];
        i = i + 1;
        if(count == i) return dest;
    }

    while(i <= count)
    {
        dest[i] = 0;
        i = i + 1;
    }

    return dest;
}


char* strcat(char* dest, char const* src)
{
    int i = 0;
    int j = 0;
    while(0 != dest[i]) i = i + 1;
    while(0 != src[j])
    {
        dest[i] = src[j];
        i = i + 1;
        j = j + 1;
    }
    dest[i] = 0;
    return dest;
}


char* strncat(char* dest, char const* src, size_t count)
{
    size_t i = 0;
    size_t j = 0;
    while(0 != dest[i]) i = i + 1;
    while(0 != src[j])
    {
        if(count == j)
        {
            dest[i] = 0;
            return dest;
        }
        dest[i] = src[j];
        i = i + 1;
        j = j + 1;
    }
    dest[i] = 0;
    return dest;
}


size_t strlen(char const* str )
{
    size_t i = 0;
    while(0 != str[i]) i = i + 1;
    return i;
}


size_t strnlen_s(char const* str, size_t strsz )
{
    size_t i = 0;
    while(0 != str[i])
    {
        if(strsz == i) return i;
        i = i + 1;
    }
    return i;
}


int strcmp(char const* lhs, char const* rhs )
{
    int i = 0;
    while(0 != lhs[i])
    {
        if(lhs[i] != rhs[i]) return lhs[i] - rhs[i];
        i = i + 1;
    }

    return lhs[i] - rhs[i];
}


int strncmp(char const* lhs, char const* rhs, size_t count)
{
    size_t i = 0;
    while(count > i)
    {
        if(0 == lhs[i]) break;
        if(lhs[i] != rhs[i]) return lhs[i] - rhs[i];
        i = i + 1;
    }

    return 0;
}


char* strchr(char const* str, int ch)
{
    char* p = str;
    while(ch != p[0])
    {
        if(0 == p[0]) return NULL;
        p = p + 1;
    }
    if(0 == p[0]) return NULL;
    return p;
}


char* strrchr(char const* str, int ch)
{
    char* p = str;
    int i = 0;
    while(0 != p[i]) i = i + 1;
    while(ch != p[i])
    {
        if(0 == i) return NULL;
        i = i - 1;
    }
    return (p + i);
}


size_t strspn(char const* dest, char const* src)
{
    if(0 == dest[0]) return 0;
    int i = 0;
    while(NULL != strchr(src, dest[i])) i = i + 1;
    return i;
}


size_t strcspn(char const* dest, char const* src)
{
    int i = 0;
    while(NULL == strchr(src, dest[i])) i = i + 1;
    return i;
}


char* strpbrk(char const* dest, char const* breakset)
{
    char* p = dest;
    char* s;
    while(0 != p[0])
    {
        s = strchr(breakset, p[0]);
        if(NULL != s) return strchr(p,  s[0]);
        p = p + 1;
    }
    return p;
}


void* memset(void* dest, int ch, size_t count)
{
    if(NULL == dest) return dest;
    size_t i = 0;
    char* s = dest;
    while(i < count)
    {
        s[i] = ch;
        i = i + 1;
    }
    return dest;
}


void* memcpy(void* dest, void const* src, size_t count)
{
    if(NULL == dest) return dest;
    if(NULL == src) return NULL;

    char* s1 = dest;
    char const* s2 = src;
    size_t i = 0;
    while(i < count)
    {
        s1[i] = s2[i];
        i = i + 1;
    }
    return dest;
}

void* memmove(void* dest, void const* src, size_t count)
{
    if (dest < src) return memcpy (dest, src, count);
    char *p = dest;
    char const *q = src;
    count = count - 1;
    while (count >= 0)
    {
        p[count] = q[count];
        count = count - 1;
    }
    return dest;
}


int memcmp(void const* lhs, void const* rhs, size_t count)
{
    if(0 == count) return 0;
    size_t i = 0;
    count = count - 1;
    char const* s1 = lhs;
    char const* s2 = rhs;
    while(i < count)
    {
        if(s1[i] != s2[i]) break;
        i = i + 1;
    }
    return (s1[i] - s2[i]);
}

File /M2libc/amd64/linux/sys/stat.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/amd64/linux/sys/stat.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/amd64/linux/sys/stat.c
/* Copyright (C) 2020 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SYS_STAT_C
#define _SYS_STAT_C

#include <sys/types.h>

#define S_IRWXU 00700
#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400

#define S_ISUID 04000
#define S_ISGID 02000
#define S_IXGRP 00010
#define S_IXOTH 00001
#define S_IRGRP 00040
#define S_IROTH 00004
#define S_IWGRP 00020
#define S_IWOTH 00002
#define S_IRWXG 00070
#define S_IRWXO 00007


int chmod(char *pathname, int mode)
{
    asm("lea_rdi,[rsp+DWORD] %16"
        "mov_rdi,[rdi]"
        "lea_rsi,[rsp+DWORD] %8"
        "mov_rsi,[rsi]"
        "mov_rax, %90"
        "syscall");
}


int fchmod(int a, mode_t b)
{
    asm("lea_rdi,[rsp+DWORD] %16"
        "mov_rdi,[rdi]"
        "lea_rsi,[rsp+DWORD] %8"
        "mov_rsi,[rsi]"
        "mov_rax, %91"
        "syscall");
}


int mkdir(char const* a, mode_t b)
{
    asm("lea_rdi,[rsp+DWORD] %16"
        "mov_rdi,[rdi]"
        "lea_rsi,[rsp+DWORD] %8"
        "mov_rsi,[rsi]"
        "mov_rax, %83"
        "syscall");
}


int mknod(char const* a, mode_t b, dev_t c)
{
    asm("lea_rdi,[rsp+DWORD] %24"
        "mov_rdi,[rdi]"
        "lea_rsi,[rsp+DWORD] %16"
        "mov_rsi,[rsi]"
        "lea_rdx,[rsp+DWORD] %8"
        "mov_rdx,[rdx]"
        "mov_rax, %133"
        "syscall");
}


mode_t umask(mode_t m)
{
    asm("lea_rdi,[rsp+DWORD] %8"
        "mov_rdi,[rdi]"
        "mov_rax, %95"
        "syscall");
}
#endif

File /M2libc/armv7l/linux/sys/stat.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/armv7l/linux/sys/stat.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/armv7l/linux/sys/stat.c
/* Copyright (C) 2020 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SYS_STAT_C
#define _SYS_STAT_C

#include <sys/types.h>

#define S_IRWXU 00700
#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400

#define S_ISUID 04000
#define S_ISGID 02000
#define S_IXGRP 00010
#define S_IXOTH 00001
#define S_IRGRP 00040
#define S_IROTH 00004
#define S_IWGRP 00020
#define S_IWOTH 00002
#define S_IRWXG 00070
#define S_IRWXO 00007


int chmod(char *pathname, int mode)
{
    asm("!15 R7 LOADI8_ALWAYS"
        "!8 R1 SUB R12 ARITH_ALWAYS"
        "!0 R1 LOAD32 R1 MEMORY"
        "!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "SYSCALL_ALWAYS");
}


int fchmod(int a, mode_t b)
{
    asm("!94 R7 LOADI8_ALWAYS"
        "!8 R1 SUB R12 ARITH_ALWAYS"
        "!0 R1 LOAD32 R1 MEMORY"
        "!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "SYSCALL_ALWAYS");
}


int mkdir(char const* a, mode_t b)
{
    asm("!39 R7 LOADI8_ALWAYS"
        "!8 R1 SUB R12 ARITH_ALWAYS"
        "!0 R1 LOAD32 R1 MEMORY"
        "!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "SYSCALL_ALWAYS");
}


int mknod(char const* a, mode_t b, dev_t c)
{
    asm("!14 R7 LOADI8_ALWAYS"
        "!12 R2 SUB R12 ARITH_ALWAYS"
        "!0 R2 LOAD32 R2 MEMORY"
        "!8 R1 SUB R12 ARITH_ALWAYS"
        "!0 R1 LOAD32 R1 MEMORY"
        "!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "SYSCALL_ALWAYS");
}


mode_t umask(mode_t m)
{
    asm("!60 R7 LOADI8_ALWAYS"
        "!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "SYSCALL_ALWAYS");
}
#endif

File /M2libc/aarch64/linux/sys/stat.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/aarch64/linux/sys/stat.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/aarch64/linux/sys/stat.c
/* Copyright (C) 2020 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SYS_STAT_C
#define _SYS_STAT_C
#include <sys/types.h>

#define S_IRWXU 00700
#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400

#define S_ISUID 04000
#define S_ISGID 02000
#define S_IXGRP 00010
#define S_IXOTH 00001
#define S_IRGRP 00040
#define S_IROTH 00004
#define S_IWGRP 00020
#define S_IWOTH 00002
#define S_IRWXG 00070
#define S_IRWXO 00007


int chmod(char *pathname, int mode)
{
    asm("SET_X0_FROM_BP" "SUB_X0_16" "DEREF_X0"
        "SET_X2_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X1_FROM_X0"
        "SET_X0_TO_0"
        "SET_X3_FROM_X0"
        "SET_X0_TO_FCNTL_H_AT_FDCWD"
        "SET_X8_TO_SYS_FCHMODAT"
        "SYSCALL");
}


int fchmod(int a, mode_t b)
{
    asm("SET_X0_FROM_BP" "SUB_X0_16" "DEREF_X0"
        "SET_X2_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X1_FROM_X0"
        "SET_X0_TO_0"
        "SET_X3_FROM_X0"
        "SET_X0_TO_FCNTL_H_AT_FDCWD"
        "SET_X8_TO_SYS_FCHMOD"
        "SYSCALL");
}


int mkdir(char const* a, mode_t b)
{
    asm("SET_X0_FROM_BP" "SUB_X0_16" "DEREF_X0"
        "SET_X2_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X1_FROM_X0"
        "SET_X0_TO_0"
        "SET_X3_FROM_X0"
        "SET_X0_TO_FCNTL_H_AT_FDCWD"
        "SET_X8_TO_SYS_MKDIRAT"
        "SYSCALL");
}


int mknod(char const* a, mode_t b, dev_t c)
{
    asm("SET_X0_TO_MINUS_1"
        "SET_X3_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_24" "DEREF_X0"
        "SET_X2_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_16" "DEREF_X0"
        "SET_X1_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_MKNOD"
        "SYSCALL");
}


mode_t umask(mode_t m)
{
    asm("SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_UMASK"
        "SYSCALL");
}
#endif

File /M2libc/riscv32/linux/sys/stat.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/riscv32/linux/sys/stat.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/riscv32/linux/sys/stat.c
/* Copyright (C) 2020 Jeremiah Orians
 * Copyright (C) 2021 Andrius Å tikonas
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SYS_STAT_C
#define _SYS_STAT_C

#include <sys/types.h>

#define S_IRWXU 00700
#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400

#define S_ISUID 04000
#define S_ISGID 02000
#define S_IXGRP 00010
#define S_IXOTH 00001
#define S_IRGRP 00040
#define S_IROTH 00004
#define S_IWGRP 00020
#define S_IWOTH 00002
#define S_IRWXG 00070
#define S_IRWXO 00007


int chmod(char *pathname, int mode)
{
        asm("rd_a0 !-100 addi" /* AT_FDCWD */
        "rd_a1 rs1_fp !-4 lw"
        "rd_a2 rs1_fp !-8 lw"
        "rd_a7 !53 addi"
        "ecall");
}


int fchmod(int a, mode_t b)
{
        asm("rd_a0 !-100 addi" /* AT_FDCWD */
        "rd_a1 rs1_fp !-4 lw"
        "rd_a2 rs1_fp !-8 lw"
        "rd_a7 !52 addi"
        "ecall");
}


int mkdir(char const* a, mode_t b)
{
        asm("rd_a0 !-100 addi" /* AT_FDCWD */
        "rd_a1 rs1_fp !-4 lw"
        "rd_a2 rs1_fp !-8 lw"
        "rd_a7 !34 addi"
        "ecall");
}


int mknod(char const* a, mode_t b, dev_t c)
{
        asm("rd_a0 !-100 addi" /* AT_FDCWD */
        "rd_a1 rs1_fp !-4 lw"
        "rd_a2 rs1_fp !-8 lw"
        "rd_a3 rs1_fp !-12 lw"
        "rd_a7 !33 addi"
        "ecall");
}


mode_t umask(mode_t m)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a7 !166 addi"
        "ecall");
}

#endif

File /M2libc/riscv64/linux/sys/stat.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/riscv64/linux/sys/stat.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/riscv64/linux/sys/stat.c
/* Copyright (C) 2020 Jeremiah Orians
 * Copyright (C) 2021 Andrius Å tikonas
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SYS_STAT_C
#define _SYS_STAT_C

#include <sys/types.h>

#define S_IRWXU 00700
#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400

#define S_ISUID 04000
#define S_ISGID 02000
#define S_IXGRP 00010
#define S_IXOTH 00001
#define S_IRGRP 00040
#define S_IROTH 00004
#define S_IWGRP 00020
#define S_IWOTH 00002
#define S_IRWXG 00070
#define S_IRWXO 00007


int chmod(char *pathname, int mode)
{
        asm("rd_a0 !-100 addi" /* AT_FDCWD */
        "rd_a1 rs1_fp !-8 ld"
        "rd_a2 rs1_fp !-16 ld"
        "rd_a7 !53 addi"
        "ecall");
}


int fchmod(int a, mode_t b)
{
        asm("rd_a0 !-100 addi" /* AT_FDCWD */
        "rd_a1 rs1_fp !-8 ld"
        "rd_a2 rs1_fp !-16 ld"
        "rd_a7 !52 addi"
        "ecall");
}


int mkdir(char const* a, mode_t b)
{
        asm("rd_a0 !-100 addi" /* AT_FDCWD */
        "rd_a1 rs1_fp !-8 ld"
        "rd_a2 rs1_fp !-16 ld"
        "rd_a7 !34 addi"
        "ecall");
}


int mknod(char const* a, mode_t b, dev_t c)
{
        asm("rd_a0 !-100 addi" /* AT_FDCWD */
        "rd_a1 rs1_fp !-8 ld"
        "rd_a2 rs1_fp !-16 ld"
        "rd_a3 rs1_fp !-24 ld"
        "rd_a7 !33 addi"
        "ecall");
}


mode_t umask(mode_t m)
{
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a7 !166 addi"
        "ecall");
}

#endif

File /M2libc/fcntl.h

Live-bootstrap source file is 'seed/stage0-posix/M2libc/fcntl.h'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/fcntl.h
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _FCNTL_H
#define _FCNTL_H

#ifdef __M2__
#include <sys/types.h>
#include <stddef.h>
#include <fcntl.c>
#else
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 00100
#define O_EXCL 00200
#define O_TRUNC 001000
#define O_APPEND 002000

#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_IRWXU 00700


extern int open(char* name, int flag, int mode);

#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif
#endif

File /M2libc/uefi/fcntl.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/uefi/fcntl.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/uefi/fcntl.c
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __FCNTL_C
#define __FCNTL_C

#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 00100
#define O_EXCL 00200
#define O_TRUNC 001000
#define O_APPEND 002000

#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_IRWXU 00700

#include <uefi/uefi.c>

void free(void* l);

int __open(struct efi_file_protocol* _rootdir, char* name, long mode, long attributes)
{
    struct efi_file_protocol* new_handle;
    char* wide_name = _posix_path_to_uefi(name);
    unsigned rval = __uefi_5(_rootdir, &new_handle, wide_name, mode, attributes, _rootdir->open);
    free(wide_name);
    if(rval != EFI_SUCCESS)
    {
        return -1;
    }
    return new_handle;
}

int _open(char* name, int flag, int mode)
{
    long mode = 0;
    long attributes = 0;
    if ((flag == (O_WRONLY | O_CREAT | O_TRUNC)) || (flag == (O_RDWR | O_CREAT | O_EXCL)))
    {
        mode = EFI_FILE_MODE_CREATE | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ;
    }
    else
    {       /* Everything else is a read */
        mode = EFI_FILE_MODE_READ;
        attributes = EFI_FILE_READ_ONLY;
    }
    return __open(_rootdir, name, mode, attributes);
}

#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif

File /M2libc/amd64/linux/fcntl.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/amd64/linux/fcntl.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/amd64/linux/fcntl.c
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __FCNTL_C
#define __FCNTL_C

#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 00100
#define O_EXCL 00200
#define O_TRUNC 001000
#define O_APPEND 002000

#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_IRWXU 00700


int _open(char* name, int flag, int mode)
{
    asm("lea_rdi,[rsp+DWORD] %24"
        "mov_rdi,[rdi]"
        "lea_rsi,[rsp+DWORD] %16"
        "mov_rsi,[rsi]"
        "lea_rdx,[rsp+DWORD] %8"
        "mov_rdx,[rdx]"
        "mov_rax, %2"
        "syscall");
}

#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif

File /M2libc/armv7l/linux/fcntl.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/armv7l/linux/fcntl.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/armv7l/linux/fcntl.c
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __FCNTL_C
#define __FCNTL_C

#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 00100
#define O_EXCL 00200
#define O_TRUNC 001000
#define O_APPEND 002000

#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_IRWXU 00700


int _open(char* name, int flag, int mode)
{
    asm("!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "!8 R1 SUB R12 ARITH_ALWAYS"
        "!0 R1 LOAD32 R1 MEMORY"
        "!12 R2 SUB R12 ARITH_ALWAYS"
        "!0 R2 LOAD32 R2 MEMORY"
        "!5 R7 LOADI8_ALWAYS"
        "SYSCALL_ALWAYS");
}

#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif

File /M2libc/aarch64/linux/fcntl.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/aarch64/linux/fcntl.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/aarch64/linux/fcntl.c
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __FCNTL_C
#define __FCNTL_C

#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 00100
#define O_EXCL 00200
#define O_TRUNC 001000
#define O_APPEND 002000

#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_IRWXU 00700


int _open(char* name, int flag, int mode)
{
    asm("SET_X0_FROM_BP" "SUB_X0_24" "DEREF_X0"
        "SET_X3_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_16" "DEREF_X0"
        "SET_X2_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X1_FROM_X0"
        "SET_X0_TO_FCNTL_H_AT_FDCWD"
        "SET_X8_TO_SYS_OPENAT"
        "SYSCALL");
}

#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif

File /M2libc/riscv32/linux/fcntl.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/riscv32/linux/fcntl.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/riscv32/linux/fcntl.c
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2021 Andrius Å tikonas
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __FCNTL_C
#define __FCNTL_C

#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 00100
#define O_EXCL 00200
#define O_TRUNC 001000
#define O_APPEND 002000

#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_IRWXU 00700


int _open(char* name, int flag, int mode)
{
    asm("rd_a0 !-100 addi" /* AT_FDCWD */
        "rd_a1 rs1_fp !-4 lw"
        "rd_a2 rs1_fp !-8 lw"
        "rd_a3 rs1_fp !-12 lw"
        "rd_a7 !56 addi"
        "ecall");
}

#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif

File /M2libc/riscv64/linux/fcntl.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/riscv64/linux/fcntl.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/riscv64/linux/fcntl.c
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2021 Andrius Å tikonas
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __FCNTL_C
#define __FCNTL_C

#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 00100
#define O_EXCL 00200
#define O_TRUNC 001000
#define O_APPEND 002000

#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_IRWXU 00700


int _open(char* name, int flag, int mode)
{
    asm("rd_a0 !-100 addi" /* AT_FDCWD */
        "rd_a1 rs1_fp !-8 ld"
        "rd_a2 rs1_fp !-16 ld"
        "rd_a3 rs1_fp !-24 ld"
        "rd_a7 !56 addi"
        "ecall");
}

#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif

File /M2libc/knight/linux/fcntl.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/knight/linux/fcntl.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/knight/linux/fcntl.c
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __FCNTL_C
#define __FCNTL_C

#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 00100
#define O_EXCL 00200
#define O_TRUNC 001000
#define O_APPEND 002000

#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_IRWXU 00700


int _open(char* name, int flag, int mode)
{
    asm("LOAD R0 R14 0"
        "LOAD R1 R14 4"
        "LOAD R2 R14 8"
        "SYS_OPEN");
}

#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif

File /M2libc/knight/native/fcntl.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/knight/native/fcntl.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/knight/native/fcntl.c
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 00100
#define O_EXCL 00200
#define O_TRUNC 001000
#define O_APPEND 002000

#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_IRWXU 00700


int _open(char* name, int flag, int mode)
{
    if((0 != flag) && (0 != mode))
    {
        asm("LOAD R0 R14 0"
            "FOPEN_WRITE");
        return 0x1101;
    }
    else
    {
        asm("LOAD R0 R14 0"
            "FOPEN_READ");
        return 0x1100;
    }
}

#define STDIN_FILENO  0x1100
#define STDOUT_FILENO 0x1101
#define STDERR_FILENO 0

File /M2libc/unistd.h

Live-bootstrap source file is 'seed/stage0-posix/M2libc/unistd.h'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/unistd.h
/* Copyright (C) 2020 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _UNISTD_H
#define _UNISTD_H
#include <sys/utsname.h>
#ifdef __M2__
#if __uefi__
#include <uefi/unistd.c>
#elif __i386__
#include <x86/linux/unistd.c>
#elif __x86_64__
#include <amd64/linux/unistd.c>
#elif __arm__
#include <armv7l/linux/unistd.c>
#elif __aarch64__
#include <aarch64/linux/unistd.c>
#elif __riscv && __riscv_xlen==32
#include <riscv32/linux/unistd.c>
#elif __riscv && __riscv_xlen==64
#include <riscv64/linux/unistd.c>
#else
#error arch not supported
#endif

#else
#define NULL 0
#define __PATH_MAX 4096

void* malloc(unsigned size);
int access(char* pathname, int mode);
int chdir(char* path);
int fchdir(int fd);
void _exit(int value);
int fork();
int waitpid (int pid, int* status_ptr, int options);
int execve(char* file_name, char** argv, char** envp);
int read(int fd, char* buf, unsigned count);
int write(int fd, char* buf, unsigned count);
int lseek(int fd, int offset, int whence);
int close(int fd);
int unlink (char *filename);
int _getcwd(char* buf, int size);
char* getcwd(char* buf, unsigned size);
char* getwd(char* buf);
char* get_current_dir_name();
int brk(void *addr);

int uname(struct utsname* unameData);

int unshare(int flags);

int geteuid();

int getegid();

int chroot(char const *path);

int mount(char const *source, char const *target, char const *filesystemtype, SCM mountflags, void const *data);

#endif
#endif

File /M2libc/uefi/unistd.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/uefi/unistd.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/uefi/unistd.c
/* Copyright (C) 2022 Andrius Å tikonas
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */


#ifndef _UNISTD_C
#define _UNISTD_C

#include <uefi/uefi.c>
#include <sys/utsname.h>
#include <stdio.h>

#define NULL 0
#define EOF 0xFFFFFFFF

/* For lseek */
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2

void* malloc(unsigned size);
size_t strlen(char const* str);
char* strncpy(char* dest, char const* src, size_t count);
char* strncat(char* dest, char const* src, size_t count);
void* memcpy(void* dest, void const* src, size_t count);

int open(char* name, int flag, int mode);
int close(int fd);
int access(char* pathname, int mode)
{
    int fd = open(pathname, 0, 0);
    if (fd == -1)
    {
        return -1;
    }
    close(fd);
    return 0;
}

int chdir(char* path)
{
    char* absolute_path = _relative_path_to_absolute(path);
    strncpy(_cwd, absolute_path, __PATH_MAX);
    if(_cwd[strlen(_cwd) - 1] != '\\')
    {
        strncat(_cwd, "/", __PATH_MAX);
    }
    free(absolute_path);
    return 0;
}

int fchdir(int fd)
{
    /* TODO: not yet implemented. */
    return -1;
}

int _get_file_size(struct efi_file_protocol* f)
{
    /* Preallocate some extra space for file_name */
    size_t file_info_size = sizeof(struct efi_file_info);
    struct efi_file_info* file_info = calloc(1, file_info_size);
    unsigned rval = __uefi_4(f, &EFI_FILE_INFO_GUID, &file_info_size, file_info, f->get_info);
    if(rval != EFI_SUCCESS)
    {
        return -1;
    }
    int file_size = file_info->file_size;
    free(file_info);
    return file_size;
}

void _set_environment(char** envp)
{
    unsigned i;
    unsigned j;
    unsigned length = _array_length(envp);
    char* name;
    char* value;
    for(i = 0; i < length; i += 1)
    {
        j = 0;
        name = envp[i];
        while(envp[i][j] != '=')
        {
            j += 1;
        }
        envp[i][j] = 0;
        value = envp[i] + j + 1;
        _set_variable(name, value);
        envp[i][j] = '=';
    }
}

FILE* fopen(char const* filename, char const* mode);
size_t fread(void* buffer, size_t size, size_t count, FILE* stream);
int fclose(FILE* stream);
int spawn(char* file_name, char** argv, char** envp)
{
    FILE* fcmd = fopen(file_name, "r");
    if(fcmd == NULL) return -1;

    long program_size = _get_file_size(fcmd->fd);

    void* executable = malloc(program_size);
    size_t count = fread(executable, 1, program_size, fcmd);
    if(count < program_size)
    {
        free(executable);
        fclose(fcmd);
        return -1;
    }
    fclose(fcmd);

    struct efi_device_path_protocol* device_path = calloc(2, sizeof(struct efi_device_path_protocol));
    device_path->type = HARDWARE_DEVICE_PATH;
    device_path->subtype = MEMORY_MAPPED;
    device_path->length = sizeof(struct efi_device_path_protocol);
    device_path->memory_type = EFI_LOADER_DATA;
    device_path->start_address = executable;
    device_path->end_address = executable + program_size;
    device_path[1].type = END_HARDWARE_DEVICE_PATH;
    device_path[1].subtype = END_ENTIRE_DEVICE_PATH;
    device_path[1].length = 4;

    void* child_ih;

    unsigned rval = __uefi_6(0, _image_handle, device_path, executable, program_size, &child_ih, _system->boot_services->load_image);
    free(device_path);
    free(executable);
    if(rval != EFI_SUCCESS) return -1;
    struct efi_loaded_image_protocol* child_image;
    rval = _open_protocol(child_ih, &EFI_LOADED_IMAGE_PROTOCOL_GUID, &child_image, child_ih, 0, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
    if(rval != EFI_SUCCESS) return -1;

    /* Concatenate char** argv array */
    unsigned arg_length = -1 ;
    unsigned i = 0;
    while(argv[i] != NULL)
    {
        arg_length += strlen(argv[i]) + 1;
        i += 1;
    }
    char* load_options = calloc(arg_length + 1, 1);
    strcpy(load_options, argv[0]);
    i = 1;
    while(argv[i] != NULL)
    {
        strcat(load_options, " ");
        strcat(load_options, argv[i]);
        i += 1;
    }
    char* uefi_path = _string2wide(load_options);

    child_image->load_options = uefi_path;
    child_image->load_options_size = 2 * arg_length;
    free(load_options);
    child_image->device = _image->device;
    rval = _close_protocol(child_ih, &EFI_LOADED_IMAGE_PROTOCOL_GUID, child_ih, 0);
    if(rval != EFI_SUCCESS) return -1;

    /* Setup environment for child process */
    _set_environment(envp);
    _set_variable("cwd", _cwd);
    _set_variable("root", _root);

    /* Run command */
    rval = __uefi_3(child_ih, 0, 0, _system->boot_services->start_image);
    free(uefi_path);

    /* Restore initial environment
     * For simplicity we just delete all variables and restore them from _envp.
     * This assumes that _envp is not modified by application, e.g. kaem.
     */
    _wipe_environment();
    _set_environment(_envp);

    return rval;
}

int fork()
{
    return -1;
}


int waitpid (int pid, int* status_ptr, int options)
{
    return -1;
}


int execve(char* file_name, char** argv, char** envp)
{
    return -1;
}

int read(int fd, char* buf, unsigned count)
{
    struct efi_file_protocol* f = fd;
    __uefi_3(fd, &count, buf, f->read);
    return count;
}

int write(int fd, char* buf, unsigned count)
{
    struct efi_file_protocol* f = fd;
    unsigned i;
    char c = 0;

    /* In UEFI StdErr might not be printing stuff to console, so just use stdout */
    if(f == STDOUT_FILENO || f == STDERR_FILENO)
    {
        for(i = 0; i < count; i += 1)
        {
            c = buf[i];
            __uefi_2(_system->con_out, &c, _system->con_out->output_string);
            if('\n' == c)
            {
                c = '\r';
                __uefi_2(_system->con_out, &c, _system->con_out->output_string);
            }
        }
        return i;
    }

    /* Otherwise write to file */
    __uefi_3(f, &count, buf, f->write);
    return count;
}


int lseek(int fd, int offset, int whence)
{
    struct efi_file_protocol* f = fd;
    if(whence == SEEK_SET)
    {
    }
    else if(whence == SEEK_CUR)
    {
        unsigned position;
        __uefi_2(f, &position, f->get_position);
        offset += position;
    }
    else if(whence == SEEK_END)
    {
        offset += _get_file_size(fd);
    }
    else
    {
        return -1;
    }

    unsigned rval = __uefi_2(f, offset, f->set_position);
    if(rval == EFI_SUCCESS)
    {
        return offset;
    }
    return -1;
}


int close(int fd)
{
    struct efi_file_protocol* f = fd;
    unsigned rval = __uefi_1(f, f->close);
    if(rval != EFI_SUCCESS)
    {
        return -1;
    }
    return rval;
}


int unlink(char* filename)
{
    FILE* f = fopen(filename, "w");
    struct efi_file_protocol* fd = f->fd;
    __uefi_1(fd, fd->delete);
}


char* getcwd(char* buf, unsigned size)
{
    size_t length = strlen(_cwd);
    if(length >= size) return NULL;
    strcpy(buf, _cwd);
    return buf;
}


char* getwd(char* buf)
{
    return getcwd(buf, __PATH_MAX);
}


char* get_current_dir_name()
{
    return getcwd(malloc(__PATH_MAX), __PATH_MAX);
}


int brk(void *addr)
{
    return -1;
}

int uname(struct utsname* unameData)
{
    memcpy(unameData->sysname, "UEFI", 5);
    memcpy(unameData->release, "1.0", 4);
    memcpy(unameData->version, "1.0", 4);
#ifdef __x86_64__
    memcpy(unameData->machine, "x86_64", 7);
#else
#error unsupported arch
#endif
}

int unshare(int flags)
{
    if (flags != 0)
    {
        return -1; // Any unshare operation is invalid
    }
    return 0;
}

int geteuid(int flags)
{
    return 0;
}

int getegid(int flags)
{
    return 0;
}

int chroot(char const *path)
{
    char *newroot = _relative_path_to_absolute(path);
    free(_root);
    _root = newroot;
    if(_root[strlen(_root) - 1] != '\\')
    {
        strncat(_root, "/", __PATH_MAX);
    }
    return 0;
}

int mount(char const *source, char const *target, char const *filesystemtype, SCM mountflags, void const *data)
{
    return -1;
}

#endif

File /M2libc/amd64/linux/unistd.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/amd64/linux/unistd.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/amd64/linux/unistd.c
/* Copyright (C) 2020 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */


#ifndef _UNISTD_C
#define _UNISTD_C
#include <sys/utsname.h>
#define NULL 0
#define __PATH_MAX 4096

void* malloc(unsigned size);

int access(char* pathname, int mode)
{
    asm("lea_rdi,[rsp+DWORD] %16"
        "mov_rdi,[rdi]"
        "lea_rsi,[rsp+DWORD] %8"
        "mov_rsi,[rsi]"
        "mov_rax, %21"
        "syscall");
}

int chdir(char* path)
{
    asm("lea_rdi,[rsp+DWORD] %8"
        "mov_rdi,[rdi]"
        "mov_rax, %80"
        "syscall");
}

int fchdir(int fd)
{
    asm("lea_rdi,[rsp+DWORD] %8"
        "mov_rdi,[rdi]"
        "mov_rax, %81"
        "syscall");
}

void _exit(int value);

int fork()
{
    asm("mov_rax, %57"
        "mov_rdi, %0"
        "syscall");
}


int waitpid (int pid, int* status_ptr, int options)
{
    /* Uses wait4 with struct rusage *ru set to NULL */
    asm("lea_rdi,[rsp+DWORD] %24"
        "mov_rdi,[rdi]"
        "lea_rsi,[rsp+DWORD] %16"
        "mov_rsi,[rsi]"
        "lea_rdx,[rsp+DWORD] %8"
        "mov_rdx,[rdx]"
        "mov_r10, %0"
        "mov_rax, %61"
        "syscall");
}


int execve(char* file_name, char** argv, char** envp)
{
    asm("lea_rdi,[rsp+DWORD] %24"
        "mov_rdi,[rdi]"
        "lea_rsi,[rsp+DWORD] %16"
        "mov_rsi,[rsi]"
        "lea_rdx,[rsp+DWORD] %8"
        "mov_rdx,[rdx]"
        "mov_rax, %59"
        "syscall");
}

int read(int fd, char* buf, unsigned count)
{ /*maybe*/
    asm("lea_rdi,[rsp+DWORD] %24"
        "mov_rdi,[rdi]"
        "lea_rsi,[rsp+DWORD] %16"
        "mov_rsi,[rsi]"
        "lea_rdx,[rsp+DWORD] %8"
        "mov_rdx,[rdx]"
        "mov_rax, %0"
        "syscall");
}

int write(int fd, char* buf, unsigned count)
{/*maybe*/
    asm("lea_rdi,[rsp+DWORD] %24"
        "mov_rdi,[rdi]"
        "lea_rsi,[rsp+DWORD] %16"
        "mov_rsi,[rsi]"
        "lea_rdx,[rsp+DWORD] %8"
        "mov_rdx,[rdx]"
        "mov_rax, %1"
        "syscall");
}

int lseek(int fd, int offset, int whence)
{
    asm("lea_rdi,[rsp+DWORD] %24"
        "mov_rdi,[rdi]"
        "lea_rsi,[rsp+DWORD] %16"
        "mov_rsi,[rsi]"
        "lea_rdx,[rsp+DWORD] %8"
        "mov_rdx,[rdx]"
        "mov_rax, %8"
        "syscall");
}


int close(int fd)
{
    asm("lea_rdi,[rsp+DWORD] %8"
        "mov_rdi,[rdi]"
        "mov_rax, %3"
        "syscall");
}


int unlink (char* filename)
{
    asm("lea_rdi,[rsp+DWORD] %8"
        "mov_rdi,[rdi]"
        "mov_rax, %87"
        "syscall");
}


int _getcwd(char* buf, int size)
{
    asm("lea_rdi,[rsp+DWORD] %16"
        "mov_rdi,[rdi]"
        "lea_rsi,[rsp+DWORD] %8"
        "mov_rsi,[rsi]"
        "mov_rax, %79"
        "syscall");
}


char* getcwd(char* buf, unsigned size)
{
    int c = _getcwd(buf, size);
    if(0 == c) return NULL;
    return buf;
}


char* getwd(char* buf)
{
    return getcwd(buf, __PATH_MAX);
}


char* get_current_dir_name()
{
    return getcwd(malloc(__PATH_MAX), __PATH_MAX);
}


int brk(void *addr)
{
    asm("mov_rax,[rsp+DWORD] %8"
        "push_rax"
        "mov_rax, %12"
        "pop_rbx"
        "mov_rdi,rbx"
        "syscall");
}

int uname(struct utsname* unameData)
{
    asm("lea_rdi,[rsp+DWORD] %8"
        "mov_rdi,[rdi]"
        "mov_rax, %63"
        "syscall");
}

int unshare(int flags)
{
    asm("lea_rdi,[rsp+DWORD] %8"
        "mov_rdi,[rdi]"
        "mov_rax, %272"
        "syscall");
}

int geteuid()
{
    asm("mov_rax, %107"
        "syscall");
}

int getegid()
{
    asm("mov_rax, %108"
        "syscall");
}

int mount(char *source, char *target, char *filesystemtype, SCM mountflags, void *data)
{
    asm("lea_rdi,[rsp+DWORD] %40"
        "mov_rdi,[rdi]"
        "lea_rsi,[rsp+DWORD] %32"
        "mov_rsi,[rsi]"
        "lea_rdx,[rsp+DWORD] %24"
        "mov_rdx,[rdx]"
        "lea_r10,[rsp+DWORD] %16"
        "mov_r10,[r10]"
        "lea_r8,[rsp+DWORD] %8"
        "mov_r8,[r8]"
        "mov_rax, %165"
        "syscall");
}

int chroot(char *path)
{
    asm("lea_rdi,[rsp+DWORD] %8"
        "mov_rdi,[rdi]"
        "mov_rax, %161"
        "syscall");
}

#endif

File /M2libc/armv7l/linux/unistd.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/armv7l/linux/unistd.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/armv7l/linux/unistd.c
/* Copyright (C) 2020 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _UNISTD_C
#define _UNISTD_C
#include <sys/utsname.h>
#define NULL 0
#define __PATH_MAX 4096

void* malloc(unsigned size);

int access(char* pathname, int mode)
{
    asm("!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "!8 R1 SUB R12 ARITH_ALWAYS"
        "!0 R1 LOAD32 R1 MEMORY"
        "!33 R7 LOADI8_ALWAYS"
        "SYSCALL_ALWAYS");
}

int chdir(char* path)
{
    asm("!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "!12 R7 LOADI8_ALWAYS"
        "SYSCALL_ALWAYS");
}

int fchdir(int fd)
{
    asm("!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "!133 R7 LOADI8_ALWAYS"
        "SYSCALL_ALWAYS");
}

void _exit(int value);

int fork()
{
    asm("!2 R7 LOADI8_ALWAYS"
        "SYSCALL_ALWAYS");
}


int waitpid (int pid, int* status_ptr, int options)
{
    asm("!114 R7 LOADI8_ALWAYS"
        "!12 R2 SUB R12 ARITH_ALWAYS"
        "!0 R2 LOAD32 R2 MEMORY"
        "!8 R1 SUB R12 ARITH_ALWAYS"
        "!0 R1 LOAD32 R1 MEMORY"
        "!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "SYSCALL_ALWAYS");
}


int execve(char* file_name, char** argv, char** envp)
{
    asm("!11 R7 LOADI8_ALWAYS"
        "!12 R2 SUB R12 ARITH_ALWAYS"
        "!0 R2 LOAD32 R2 MEMORY"
        "!8 R1 SUB R12 ARITH_ALWAYS"
        "!0 R1 LOAD32 R1 MEMORY"
        "!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "SYSCALL_ALWAYS");
}

int read(int fd, char* buf, unsigned count)
{
    asm("!3 R7 LOADI8_ALWAYS"
        "!12 R2 SUB R12 ARITH_ALWAYS"
        "!0 R2 LOAD32 R2 MEMORY"
        "!8 R1 SUB R12 ARITH_ALWAYS"
        "!0 R1 LOAD32 R1 MEMORY"
        "!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "SYSCALL_ALWAYS");
}

int write(int fd, char* buf, unsigned count)
{
    asm("!4 R7 LOADI8_ALWAYS"
        "!12 R2 SUB R12 ARITH_ALWAYS"
        "!0 R2 LOAD32 R2 MEMORY"
        "!8 R1 SUB R12 ARITH_ALWAYS"
        "!0 R1 LOAD32 R1 MEMORY"
        "!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "SYSCALL_ALWAYS");
}

int lseek(int fd, int offset, int whence)
{
    asm("!19 R7 LOADI8_ALWAYS"
        "!12 R2 SUB R12 ARITH_ALWAYS"
        "!0 R2 LOAD32 R2 MEMORY"
        "!8 R1 SUB R12 ARITH_ALWAYS"
        "!0 R1 LOAD32 R1 MEMORY"
        "!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "SYSCALL_ALWAYS");
}


int close(int fd)
{
    asm("!4 R0 SUB R12 ARITH_ALWAYS"
        "!6 R7 LOADI8_ALWAYS"
        "SYSCALL_ALWAYS");
}


int unlink (char* filename)
{
    asm("!4 R0 SUB R12 ARITH_ALWAYS"
        "!10 R7 LOADI8_ALWAYS"
        "SYSCALL_ALWAYS");
}


int _getcwd(char* buf, int size)
{
    asm("!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "!8 R1 SUB R12 ARITH_ALWAYS"
        "!0 R1 LOAD32 R1 MEMORY"
        "!183 R7 LOADI8_ALWAYS"
        "SYSCALL_ALWAYS");
}


char* getcwd(char* buf, unsigned size)
{
    int c = _getcwd(buf, size);
    if(0 == c) return NULL;
    return buf;
}


char* getwd(char* buf)
{
    return getcwd(buf, __PATH_MAX);
}


char* get_current_dir_name()
{
    return getcwd(malloc(__PATH_MAX), __PATH_MAX);
}


int brk(void *addr)
{
    asm("!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "!45 R7 LOADI8_ALWAYS"
        "SYSCALL_ALWAYS");
}

int uname(struct utsname* unameData)
{
    asm("!122 R7 LOADI8_ALWAYS"
        "!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "SYSCALL_ALWAYS");
}

int unshare(int flags)
{
    asm("!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "!337 R7 LOADI8_ALWAYS"
        "SYSCALL_ALWAYS");
}

int geteuid()
{
    asm("!201 R7 LOADI8_ALWAYS"
        "SYSCALL_ALWAYS");
}

int getegid()
{
    asm("!202 R7 LOADI8_ALWAYS"
       "SYSCALL_ALWAYS");
}

int chroot(char const *path)
{
    asm("!4 R0 SUB R12 ARITH_ALWAYS"
       "!61 R7 LOADI8_ALWAYS"
       "SYSCALL_ALWAYS");
}

int mount(char const *source, char const *target, char const *filesystemtype, SCM mountflags, void const *data)
{
    asm("!4 R0 SUB R12 ARITH_ALWAYS"
        "!0 R0 LOAD32 R0 MEMORY"
        "!8 R1 SUB R12 ARITH_ALWAYS"
        "!0 R1 LOAD32 R1 MEMORY"
        "!12 R2 SUB R12 ARITH_ALWAYS"
        "!0 R2 LOAD32 R2 MEMORY"
        "!16 R3 SUB R12 ARITH_ALWAYS"
        "!0 R3 LOAD32 R3 MEMORY"
        "!20 R4 SUB R12 ARITH_ALWAYS"
        "!0 R4 LOAD32 R4 MEMORY"
        "!31 R7 LOADI8_ALWAYS"
        "SYSCALL_ALWAYS");
}

#endif

File /M2libc/aarch64/linux/unistd.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/aarch64/linux/unistd.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/aarch64/linux/unistd.c
/* Copyright (C) 2020 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _UNISTD_C
#define _UNISTD_C
#include <sys/utsname.h>

#define NULL 0
#define __PATH_MAX 4096

void* malloc(unsigned size);

int access(char* pathname, int mode)
{
    asm("SET_X0_FROM_BP" "SUB_X0_16" "DEREF_X0"
        "SET_X2_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X1_FROM_X0"
        "SET_X0_TO_0"
        "SET_X3_FROM_X0"
        "SET_X0_TO_FCNTL_H_AT_FDCWD"
        "SET_X8_TO_SYS_FACCESSAT"
        "SYSCALL");
}

int chdir(char* path)
{
    asm("SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_CHDIR"
        "SYSCALL");
}

int fchdir(int fd)
{
    asm("SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_FCHDIR"
        "SYSCALL");
}

void _exit(int value);

int fork()
{
    asm("SET_X0_TO_0"
        "SET_X1_FROM_X0"
        "SET_X2_FROM_X0"
        "SET_X3_FROM_X0"
        "SET_X4_FROM_X0"
        "SET_X5_FROM_X0"
        "SET_X6_FROM_X0"
        "SET_X0_TO_17"
        "SET_X8_TO_SYS_CLONE"
        "SYSCALL");
}


int waitpid (int pid, int* status_ptr, int options)
{
    asm("SET_X0_TO_MINUS_1"
        "SET_X3_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_24" "DEREF_X0"
        "SET_X2_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_16" "DEREF_X0"
        "SET_X1_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_WAIT4"
        "SYSCALL");
}


int execve(char* file_name, char** argv, char** envp)
{
    asm("SET_X0_FROM_BP" "SUB_X0_24" "DEREF_X0"
        "SET_X2_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_16" "DEREF_X0"
        "SET_X1_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_EXECVE"
        "SYSCALL");
}

int read(int fd, char* buf, unsigned count)
{
    asm("SET_X0_FROM_BP" "SUB_X0_24" "DEREF_X0"
        "SET_X2_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_16" "DEREF_X0"
        "SET_X1_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_READ"
        "SYSCALL");
}

int write(int fd, char* buf, unsigned count)
{
    asm("SET_X0_FROM_BP" "SUB_X0_24" "DEREF_X0"
        "SET_X2_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_16" "DEREF_X0"
        "SET_X1_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_WRITE"
        "SYSCALL");
}

int lseek(int fd, int offset, int whence)
{
    asm("SET_X0_TO_MINUS_1"
        "SET_X3_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_24" "DEREF_X0"
        "SET_X2_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_16" "DEREF_X0"
        "SET_X1_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_LSEEK"
        "SYSCALL");
}


int close(int fd)
{
    asm("SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_CLOSE"
        "SYSCALL");
}


int unlink (char* filename)
{
    asm("SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_UNLINK"
        "SYSCALL");
}


int _getcwd(char* buf, int size)
{
    asm("SET_X0_FROM_BP" "SUB_X0_16" "DEREF_X0"
        "SET_X1_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_GETCWD"
        "SYSCALL");
}


char* getcwd(char* buf, unsigned size)
{
    int c = _getcwd(buf, size);
    if(0 == c) return NULL;
    return buf;
}


char* getwd(char* buf)
{
    return getcwd(buf, __PATH_MAX);
}


char* get_current_dir_name()
{
    return getcwd(malloc(__PATH_MAX), __PATH_MAX);
}


int brk(void *addr)
{
    asm("SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_BRK"
        "SYSCALL");
}

int uname(struct utsname* unameData)
{
    asm("SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_UNAME"
        "SYSCALL");
}

int unshare(int flags)
{
    asm("SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_UNSHARE"
        "SYSCALL");
}

int geteuid()
{
    asm("SET_X8_TO_SYS_GETEUID"
        "SYSCALL");
}

int getegid()
{
    asm("SET_X8_TO_SYS_GETEGID"
        "SYSCALL");
}

int chroot(char const *path)
{
    asm("SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_CHROOT"
        "SYSCALL");
}

int mount(char const *source, char const *target, char const *filesystemtype, SCM mountflags, void const *data)
{
    asm("SET_X0_FROM_BP" "SUB_X0_40" "DEREF_X0"
        "SET_X4_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_32" "DEREF_X0"
        "SET_X3_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_24" "DEREF_X0"
        "SET_X2_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_16" "DEREF_X0"
        "SET_X1_FROM_X0"
        "SET_X0_FROM_BP" "SUB_X0_8" "DEREF_X0"
        "SET_X8_TO_SYS_MOUNT"
        "SYSCALL");
}

#endif

File /M2libc/riscv32/linux/unistd.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/riscv32/linux/unistd.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/riscv32/linux/unistd.c
/* Copyright (C) 2020 Jeremiah Orians
 * Copyright (C) 2021 Andrius Å tikonas
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _UNISTD_C
#define _UNISTD_C

#include <signal.h>
#include <sys/utsname.h>
#define NULL 0
#define __PATH_MAX 4096

#define P_PID 1
#define WEXITED 4
#define __SI_SWAP_ERRNO_CODE

void* malloc(unsigned size);

int access(char* pathname, int mode)
{
    asm("rd_a0 !-100 addi" /* AT_FDCWD */
        "rd_a1 rs1_fp !-4 lw"
        "rd_a2 rs1_fp !-8 lw"
        "rd_a3 addi" /* flags = 0 */
        "rd_a7 !48 addi"
        "ecall");
}

int chdir(char* path)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a7 !49 addi"
        "ecall");
}

int fchdir(int fd)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a7 !50 addi"
        "ecall");
}

void _exit(int value);

int fork()
{
    asm("rd_a7 !220 addi"
        "rd_a0 !17 addi" /* SIGCHLD */
        "rd_a1 mv"       /* Child uses duplicate of parent's stack */
        "ecall");
}

int waitid(int idtype, int id, struct siginfo_t *infop, int options, void *rusage)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a1 rs1_fp !-8 lw"
        "rd_a2 rs1_fp !-12 lw"
        "rd_a3 rs1_fp !-16 lw"
        "rd_a4 rs1_fp !-20 lw"
        "rd_a7 !95 addi"
        "ecall");
}

void* calloc(int count, int size);
void free(void* l);
struct siginfo_t *__waitpid_info;
int waitpid(int pid, int* status_ptr, int options)
{
    if(NULL == __waitpid_info) __waitpid_info = calloc(1, sizeof(struct siginfo_t));
    int r = waitid(P_PID, pid, __waitpid_info, options|WEXITED, NULL);

    if(__waitpid_info->si_pid != 0)
    {
        int sw = 0;
        if(__waitpid_info->si_code == CLD_EXITED)
        {
            sw = (__waitpid_info->si_status & 0xff) << 8;
        }
        else if(__waitpid_info->si_code == CLD_KILLED)
        {
            sw = __waitpid_info->si_status & 0x7f;
        }
        else if(__waitpid_info->si_code == CLD_DUMPED)
        {
            sw = (__waitpid_info->si_status & 0x7f) | 0x80;
        }
        else if(__waitpid_info->si_code == CLD_CONTINUED)
        {
            sw = 0xffff;
        }
        else if(__waitpid_info->si_code == CLD_STOPPED || __waitpid_info->si_code == CLD_TRAPPED)
        {
            sw = ((__waitpid_info->si_status & 0xff) << 8) + 0x7f;
        }
        if(status_ptr != NULL) *status_ptr = sw;
    }
    int rval = __waitpid_info->si_pid;

    if(r < 0)
    {
        return r;
    }
    return rval;
}

int execve(char* file_name, char** argv, char** envp)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a1 rs1_fp !-8 lw"
        "rd_a2 rs1_fp !-12 lw"
        "rd_a7 !221 addi"
        "ecall");
}

int read(int fd, char* buf, unsigned count)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a1 rs1_fp !-8 lw"
        "rd_a2 rs1_fp !-12 lw"
        "rd_a7 !63 addi"
        "ecall");
}

int write(int fd, char* buf, unsigned count)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a1 rs1_fp !-8 lw"
        "rd_a2 rs1_fp !-12 lw"
        "rd_a7 !64 addi"
        "ecall");
}

int llseek(int fd, int offset_high, int offset_low, int result, int whence)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a1 rs1_fp !-8 lw"
        "rd_a2 rs1_fp !-12 lw"
        "rd_a3 rs1_fp !-16 lw"
        "rd_a4 rs1_fp !-20 lw"
        "rd_a7 !62 addi"
        "ecall");
}

int lseek(int fd, int offset, int whence)
{
    int result;
    if(llseek(fd, offset >> 32, offset, &result, whence))
    {
        return -1;
    }
    return result;
}

int close(int fd)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a7 !57 addi"    /* close */
        "ecall");
}


int unlink (char* filename)
{
    asm("rd_a0 !-100 addi" /* AT_FDCWD */
        "rd_a1 rs1_fp !-4 lw"
        "rd_a2 !0 addi"     /* No flags */
        "rd_a7 !35 addi"    /* unlinkat */
        "ecall");
}


int _getcwd(char* buf, int size)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a1 rs1_fp !-8 lw"
        "rd_a7 !17 addi"
        "ecall");
}


char* getcwd(char* buf, unsigned size)
{
    int c = _getcwd(buf, size);
    if(0 == c) return NULL;
    return buf;
}


char* getwd(char* buf)
{
    return getcwd(buf, __PATH_MAX);
}


char* get_current_dir_name()
{
    return getcwd(malloc(__PATH_MAX), __PATH_MAX);
}


int brk(void *addr)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a7 !214 addi"
        "ecall");
}

int uname(struct utsname* unameData)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a7 !160 addi"
        "ecall");
}

int unshare(int flags)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a7 !97 addi"
        "ecall");
}

int geteuid()
{
    asm("rd_a7 !175 addi"
        "ecall");
}

int getegid()
{
    asm("rd_a7 !177 addi"
        "ecall");
}

int mount (char *source, char *target, char *filesystemtype, SCM mountflags, void *data)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a1 rs1_fp !-8 lw"
        "rd_a2 rs1_fp !-12 lw"
        "rd_a3 rs1_fp !-16 lw"
        "rd_a4 rs1_fp !-20 lw"
        "rd_a7 !40 addi"
        "ecall");
}

int chroot(char *path)
{
    asm("rd_a0 rs1_fp !-4 lw"
        "rd_a7 !51 addi"
        "ecall");
}

#endif

File /M2libc/signal.h

Live-bootstrap source file is 'seed/stage0-posix/M2libc/signal.h'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/signal.h
/* Copyright (C) 2021 Andrius Å tikonas
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SIGNAL_H
#define _SIGNAL_H

#define CLD_EXITED      1       /* child has exited */
#define CLD_KILLED      2       /* child was killed */
#define CLD_DUMPED      3       /* child terminated abnormally */
#define CLD_TRAPPED     4       /* traced child has trapped */
#define CLD_STOPPED     5       /* child has stopped */
#define CLD_CONTINUED   6       /* stopped child has continued */

struct siginfo_t {
    int si_signo;
#ifdef __SI_SWAP_ERRNO_CODE
    int si_code;
    int si_errno;
#else
    int si_errno;
    int si_code;
#endif
    int si_pid;
    int si_uid;
    int si_status;
    int si_utime;
    int si_stime;
};

#endif

File /M2libc/riscv64/linux/unistd.c

Live-bootstrap source file is 'seed/stage0-posix/M2libc/riscv64/linux/unistd.c'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/riscv64/linux/unistd.c
/* Copyright (C) 2020 Jeremiah Orians
 * Copyright (C) 2021 Andrius Å tikonas
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _UNISTD_C
#define _UNISTD_C
#include <sys/utsname.h>
#define NULL 0
#define __PATH_MAX 4096
#define __SI_SWAP_ERRNO_CODE

void* malloc(unsigned size);

int access(char* pathname, int mode)
{
    asm("rd_a0 !-100 addi" /* AT_FDCWD */
        "rd_a1 rs1_fp !-8 ld"
        "rd_a2 rs1_fp !-16 ld"
        "rd_a3 addi" /* flags = 0 */
        "rd_a7 !48 addi"
        "ecall");
}

int chdir(char* path)
{
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a7 !49 addi"
        "ecall");
}

int fchdir(int fd)
{
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a7 !50 addi"
        "ecall");
}

void _exit(int value);

int fork()
{
    asm("rd_a7 !220 addi"
        "rd_a0 !17 addi" /* SIGCHld */
        "rd_a1 mv"       /* Child uses duplicate of parent's stack */
        "ecall");
}


int waitpid (int pid, int* status_ptr, int options)
{
    /* Uses wait4 with struct rusage *ru set to NULL */
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a1 rs1_fp !-16 ld"
        "rd_a2 rs1_fp !-24 ld"
        "rd_a3 addi"
        "rd_a7 !260 addi"
        "ecall");
}


int execve(char* file_name, char** argv, char** envp)
{
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a1 rs1_fp !-16 ld"
        "rd_a2 rs1_fp !-24 ld"
        "rd_a7 !221 addi"
        "ecall");
}

int read(int fd, char* buf, unsigned count)
{
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a1 rs1_fp !-16 ld"
        "rd_a2 rs1_fp !-24 ld"
        "rd_a7 !63 addi"
        "ecall");
}

int write(int fd, char* buf, unsigned count)
{
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a1 rs1_fp !-16 ld"
        "rd_a2 rs1_fp !-24 ld"
        "rd_a7 !64 addi"
        "ecall");
}

int lseek(int fd, int offset, int whence)
{
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a1 rs1_fp !-16 ld"
        "rd_a2 rs1_fp !-24 ld"
        "rd_a7 !62 addi"
        "ecall");
}


int close(int fd)
{
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a7 !57 addi"    /* close */
        "ecall");
}


int unlink (char* filename)
{
    asm("rd_a0 !-100 addi" /* AT_FDCWD */
        "rd_a1 rs1_fp !-8 ld"
        "rd_a2 !0 addi"     /* No flags */
        "rd_a7 !35 addi"    /* unlinkat */
        "ecall");
}


int _getcwd(char* buf, int size)
{
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a1 rs1_fp !-16 ld"
        "rd_a7 !17 addi"
        "ecall");
}


char* getcwd(char* buf, unsigned size)
{
    int c = _getcwd(buf, size);
    if(0 == c) return NULL;
    return buf;
}


char* getwd(char* buf)
{
    return getcwd(buf, __PATH_MAX);
}


char* get_current_dir_name()
{
    return getcwd(malloc(__PATH_MAX), __PATH_MAX);
}


int brk(void *addr)
{
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a7 !214 addi"
        "ecall");
}

int uname(struct utsname* unameData)
{
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a7 !160 addi"
        "ecall");
}

int unshare(int flags)
{
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a7 !97 addi"
        "ecall");
}

int geteuid()
{
    asm("rd_a7 !175 addi"
        "ecall");
}

int getegid()
{
    asm("rd_a7 !177 addi"
        "ecall");
}

int mount(char *source, char *target, char *filesystemtype, SCM mountflags, void *data)
{
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a1 rs1_fp !-16 ld"
        "rd_a2 rs1_fp !-24 ld"
        "rd_a3 rs1_fp !-32 ld"
        "rd_a4 rs1_fp !-40 ld"
        "rd_a7 !40 addi"
        "ecall");
}

int chroot(char *path)
{
    asm("rd_a0 rs1_fp !-8 ld"
        "rd_a7 !51 addi"
        "ecall");
}

#endif

File /M2libc/stdlib.h

Live-bootstrap source file is 'seed/stage0-posix/M2libc/stdlib.h'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/stdlib.h
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _STDLIB_H
#define _STDLIB_H
#include <unistd.h>

#ifdef __M2__
#include <stdlib.c>
#else


#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0

extern void exit(int value);

extern long _malloc_ptr;
extern long _brk_ptr;

extern void free(void* l);
extern void* malloc(unsigned size);
extern void* memset(void* ptr, int value, int num);
extern void* calloc(int count, int size);
extern char *getenv(const char *name);

size_t wcstombs(char* dest, const wchar_t* src, size_t n);

#endif
#endif

File /M2libc/bootstrappable.h

Live-bootstrap source file is 'seed/stage0-posix/M2libc/bootstrappable.h'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/bootstrappable.h
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _BOOTSTRAPPABLE_H
#define _BOOTSTRAPPABLE_H

/* Essential common CONSTANTS*/
#define TRUE 1
#define FALSE 0

#ifdef __M2__
#include <bootstrappable.c>
#else
/* Universally useful functions */
void require(int bool, char* error);
int match(char* a, char* b);
int in_set(int c, char* s);
int strtoint(char *a);
char* int2str(int x, int base, int signed_p);

#endif
#endif

File /x86/artifact/M2-Mesoplanet-000000

(Source not found at '(null)')

File /x86/artifact/M2-Planet-000000

(Source not found at '(null)')

File /x86/artifact/blood-elf-000000

(Source not found at '(null)')

File /x86/artifact/M1-macro-000000

(Source not found at '(null)')

File /mescc-tools-extra/match.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools-extra/match.c'.
URL: https://github.com/oriansj/mescc-tools-extra/blob/460648be66c4b4e4eeb138b8b88d5d6e0077e79e/match.c
/* Copyright (C) 2021 Andrius Å tikonas
 * This file is part of mescc-tools-extra
 *
 * mescc-tools-extra is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools-extra is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools-extra.  If not, see <http://www.gnu.org/licenses/>.
 */

/********************************************************************************
 * "match" can be used to compare strings. It is useful to write conditional    *
 * code in kaem.                                                                *
 *                                                                              *
 * Usage: match string1 string2                                                 *
 * Returns: 0 if strings match                                                  *
 ********************************************************************************/

#include <stdio.h>
#include <string.h>

#include "M2libc/bootstrappable.h"

int main(int argc, char **argv)
{
    /* ensure correct number of arguments */
    if(argc != 3)
    {
        fputs("match needs exactly 2 arguments.\n", stderr);
        return 2;
    }

    /* deal with badly behaving shells calling */
    if(NULL == argv[1])
    {
        fputs("You passed a null string\n", stderr);
        return 3;
    }
    if(NULL == argv[2])
    {
        fputs("You passed a null string\n", stderr);
        return 3;
    }

    return !match(argv[1], argv[2]);
}

File /mescc-tools-extra/mkdir.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools-extra/mkdir.c'.
URL: https://github.com/oriansj/mescc-tools-extra/blob/460648be66c4b4e4eeb138b8b88d5d6e0077e79e/mkdir.c
/* Copyright (C) 2009 Tim Kientzle
 * Copyright (C) 2021 Jeremiah Orians
 * Copyright (C) 2021 Andrius Å tikonas
 * This file is part of mescc-tools-extra
 *
 * mescc-tools-extra is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools-extra is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools-extra.  If not, see <http://www.gnu.org/licenses/>.
 */

/********************************************************************************
 * "mkdir" can be used to create empty directories. It can also create          *
 * required parent directories.                                                 *
 *                                                                              *
 * Usage: mkdir <dir1>/<dir2> <dir3>                                            *
 *                                                                              *
 * These are all highly standard and portable headers.                          *
 ********************************************************************************/
#include <stdio.h>
#include <string.h>

/* This is for mkdir(); this may need to be changed for some platforms. */
#include <sys/stat.h>  /* For mkdir() */
#include <stdlib.h>
#include "M2libc/bootstrappable.h"

#define MAX_STRING 4096

int parents;

/* Create a directory, including parent directories as necessary. */
void create_dir(char *pathname, int mode)
{
    char *p;
    int r;

    /* Strip trailing '/' */
    if(pathname[strlen(pathname) - 1] == '/')
    {
        pathname[strlen(pathname) - 1] = '\0';
    }

    /* Try creating the directory. */
    r = mkdir(pathname, mode);

    if((r != 0) && parents)
    {
        /* On failure, try creating parent directory. */
        p = strrchr(pathname, '/');

        if(p != NULL)
        {
            p[0] = '\0';
            create_dir(pathname, mode);
            p[0] = '/';
            r = mkdir(pathname, mode);
        }
    }

    if((r != 0) && !parents)
    {
        fputs("Could not create directory ", stderr);
        fputs(pathname, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char **argv)
{
    /* This adds some quasi-compatibility with GNU coreutils' mkdir. */
    parents = FALSE;
    int i;
    int mode = 0755;
    char* raw_mode = NULL;

    for(i = 1; argc > i; i = i + 1)
    {
        if(match(argv[i], "-p") || match(argv[i], "--parents"))
        {
            parents = TRUE;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs("mescc-tools-extra mkdir supports --parents and --mode 0750 "
                  "but the last argument always must be the directly to make\n", stdout);
            return 0;
        }
        else if(match(argv[i], "-v") || match(argv[i], "--version"))
        {
            fputs("mescc-tools-extra mkdir version 1.3.0\n", stdout);
            return 0;
        }
        else if(match(argv[i], "-m") || match(argv[i], "--mode"))
        {
            raw_mode = calloc(MAX_STRING, sizeof(char));
            require(raw_mode != NULL, "Memory initialization of mode failed\n");
            /* We need to indicate it is octal */
            strcat(raw_mode, "0");
            strcat(raw_mode, argv[i+1]);
            mode = strtoint(raw_mode);
            i = i + 1;
        }
        else create_dir(argv[i], mode);
    }

    return 0;
}

File /mescc-tools-extra/untar.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools-extra/untar.c'.
URL: https://github.com/oriansj/mescc-tools-extra/blob/460648be66c4b4e4eeb138b8b88d5d6e0077e79e/untar.c
/* Copyright (C) 2009 Tim Kientzle
 * Copyright (C) 2021 Jeremiah Orians
 * This file is part of mescc-tools-extra
 *
 * mescc-tools-extra is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools-extra is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools-extra.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * "untar" is an extremely simple tar extractor:
 *  * A single C source file, so it should be easy to compile
 *    and run on any system with a C compiler.
 *  * Extremely portable standard C.  The only non-ANSI function
 *    used is mkdir().
 *  * Reads basic ustar tar archives.
 *  * Does not require libarchive or any other special library.
 *
 * To compile: cc -o untar untar.c
 *
 * Usage:  untar <archive>
 *
 * In particular, this program should be sufficient to extract the
 * distribution for libarchive, allowing people to bootstrap
 * libarchive on systems that do not already have a tar program.
 *
 * To unpack libarchive-x.y.z.tar.gz:
 *    * gunzip libarchive-x.y.z.tar.gz
 *    * untar libarchive-x.y.z.tar
 *
 * Written by Tim Kientzle, March 2009.
 *
 * Released into the public domain.
 */

/* These are all highly standard and portable headers. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* This is for mkdir(); this may need to be changed for some platforms. */
#include <sys/stat.h>  /* For mkdir() */
#include "M2libc/bootstrappable.h"

int FUZZING;
int VERBOSE;
int STRICT;

/* Parse an octal number, ignoring leading and trailing nonsense. */
int parseoct(char const* p, size_t n)
{
    int i = 0;
    int h;

    while(((p[0] < '0') || (p[0] > '7')) && (n > 0))
    {
        p = p + 1;
        n = n - 1;
    }

    while((p[0] >= '0') && (p[0] <= '7') && (n > 0))
    {
        i = i << 3;
        h = p[0];
        i = i + h - 48;
        p = p + 1;
        n = n - 1;
    }

    return i;
}

/* Returns true if this is 512 zero bytes. */
int is_end_of_archive(char const* p)
{
    int n;

    for(n = 511; n >= 0; n = n - 1)
    {
        if(p[n] != 0)
        {
            return FALSE;
        }
    }

    return TRUE;
}

/* Create a directory, including parent directories as necessary. */
void create_dir(char *pathname, int mode)
{
    char *p;
    int r;

    /* Strip trailing '/' */
    if(pathname[strlen(pathname) - 1] == '/')
    {
        pathname[strlen(pathname) - 1] = '\0';
    }

    /* Try creating the directory. */
    if(!FUZZING)
    {
        r = mkdir(pathname, mode);

        if(r != 0)
        {
            /* On failure, try creating parent directory. */
            p = strrchr(pathname, '/');

            if(p != NULL)
            {
                p[0] = '\0';
                create_dir(pathname, 0755);
                p[0] = '/';
                r = mkdir(pathname, mode);
            }
        }

        if(r != 0)
        {
            fputs("Could not create directory ", stderr);
            fputs(pathname, stderr);
            fputc('\n', stderr);
        }
    }
}

/* Create a file, including parent directory as necessary. */
FILE* create_file(char *pathname)
{
    if(FUZZING) return NULL;
    FILE* f;
    f = fopen(pathname, "w");

    if(f == NULL)
    {
        /* Try creating parent dir and then creating file. */
        char *p = strrchr(pathname, '/');

        if(p != NULL)
        {
            p[0] = '\0';
            create_dir(pathname, 0755);
            p[0] = '/';
            f = fopen(pathname, "w");
        }
    }

    return f;
}

/* Verify the tar checksum. */
int verify_checksum(char const* p)
{
    int n;
    int u = 0;
    unsigned h;

    for(n = 0; n < 512; n = n + 1)
    {
        /* Standard tar checksum adds unsigned bytes. */
        if((n < 148) || (n > 155))
        {
            h = p[n];
            u = u + h;
        }
        else
        {
            u = u + 0x20;
        }
    }

    int r = parseoct(p + 148, 8);

    return (u == r);
}

/* Extract a tar archive. */
int untar(FILE *a, char const* path)
{
    char* buff = calloc(514, sizeof(char));
    FILE* f = NULL;
    size_t bytes_read;
    size_t bytes_written;
    int filesize;
    int op;
    if(VERBOSE)
    {
        fputs("Extracting from ", stdout);
        puts(path);
    }

    while(TRUE)
    {
        memset(buff, 0, 514);
        bytes_read = fread(buff, sizeof(char), 512, a);

        if(bytes_read < 512)
        {
            fputs("Short read on ", stderr);
            fputs(path, stderr);
            fputs(": expected 512, got ", stderr);
            fputs(int2str(bytes_read, 10, TRUE), stderr);
            fputc('\n', stderr);
            return FALSE;
        }

        if(is_end_of_archive(buff))
        {
            if(VERBOSE)
            {
                fputs("End of ", stdout);
                puts(path);
            }
            return TRUE;
        }

        if(!verify_checksum(buff))
        {
            fputs("Checksum failure\n", stderr);
            return FALSE;
        }

        filesize = parseoct(buff + 124, 12);

        op = buff[156];
        if('1' == op)
        {
            if(STRICT)
            {
                fputs("unable to create hardlinks\n", stderr);
                exit(EXIT_FAILURE);
            }
            fputs(" Ignoring hardlink ", stdout);
            puts(buff);
        }
        else if('2' == op)
        {
            if(STRICT)
            {
                fputs("unable to create symlinks\n", stderr);
                exit(EXIT_FAILURE);
            }
            fputs(" Ignoring symlink ", stdout);
            puts(buff);
        }
        else if('3' == op)
        {
            if(STRICT)
            {
                fputs("unable to create character devices\n", stderr);
                exit(EXIT_FAILURE);
            }
            fputs(" Ignoring character device ", stdout);
            puts(buff);
        }
        else if('4' == op)
        {
            if(STRICT)
            {
                fputs("unable to create block devices\n", stderr);
                exit(EXIT_FAILURE);
            }
            fputs(" Ignoring block device ", stdout);
            puts(buff);
        }
        else if('5' == op)
        {
            if(VERBOSE)
            {
                fputs(" Extracting dir ", stdout);
                puts(buff);
            }
            create_dir(buff, parseoct(buff + 100, 8));
            filesize = 0;
        }
        else if('6' == op)
        {
            if(STRICT)
            {
                fputs("unable to create FIFO\n", stderr);
                exit(EXIT_FAILURE);
            }
            fputs(" Ignoring FIFO ", stdout);
            puts(buff);
        }
        else
        {
            if(VERBOSE)
            {
                fputs(" Extracting file ", stdout);
                puts(buff);
            }
            f = create_file(buff);
        }

        while(filesize > 0)
        {
            bytes_read = fread(buff, 1, 512, a);

            if(bytes_read < 512)
            {
                fputs("Short read on ", stderr);
                fputs(path, stderr);
                fputs(": Expected 512, got ", stderr);
                puts(int2str(bytes_read, 10, TRUE));
                return FALSE;
            }

            if(filesize < 512)
            {
                bytes_read = filesize;
            }

            if(f != NULL)
            {
                if(!FUZZING)
                {
                    bytes_written = fwrite(buff, 1, bytes_read, f);
                    if(bytes_written != bytes_read)
                    {
                        fputs("Failed write\n", stderr);
                        fclose(f);
                        f = NULL;
                    }
                }
            }

            filesize = filesize - bytes_read;
        }

        if(f != NULL)
        {
            fclose(f);
            f = NULL;
        }
    }
    return TRUE;
}

struct files_queue
{
    char* name;
    FILE* f;
    struct files_queue* next;
};

int main(int argc, char **argv)
{
    struct files_queue* list = NULL;
    struct files_queue* a;
    STRICT = TRUE;
    FUZZING = FALSE;
    int r;

    int i = 1;
    while (i < argc)
    {
        if(NULL == argv[i])
        {
            i = i + 1;
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            a = calloc(1, sizeof(struct files_queue));
            require(NULL != a, "failed to allocate enough memory to even get the file name\n");
            a->next = list;
            a->name = argv[i+1];
            require(NULL != a->name, "the --file option requires a filename to be given\n");
            a->f = fopen(a->name, "r");
            if(a->f == NULL)
            {
                fputs("Unable to open ", stderr);
                fputs(a->name, stderr);
                fputc('\n', stderr);
                if(STRICT) exit(EXIT_FAILURE);
            }
            list = a;
            i = i + 2;
        }
        else if(match(argv[i], "--chaos") || match(argv[i], "--fuzz-mode") || match(argv[i], "--fuzzing"))
        {
            FUZZING = TRUE;
            fputs("fuzz-mode enabled, preparing for chaos\n", stderr);
            i = i + 1;
        }
        else if(match(argv[i], "-v") || match(argv[i], "--verbose"))
        {
            VERBOSE = TRUE;
            i = i + 1;
        }

        else if(match(argv[i], "--non-strict") || match(argv[i], "--bad-decisions-mode") || match(argv[i], "--drunk-mode"))
        {
            STRICT = FALSE;
            fputs("non-strict mode enabled, preparing for chaos\n", stderr);
            i = i + 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" --file $input.gz\n", stderr);
            fputs("--verbose to print list of extracted files\n", stderr);
            fputs("--help to get this message\n", stderr);
            fputs("--fuzz-mode if you wish to fuzz this application safely\n", stderr);
            fputs("--non-strict if you wish to just ignore files not existing\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option:", stderr);
            fputs(argv[i], stderr);
            fputs("\nAborting to avoid problems\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    /* Process the queue one file at a time */
    while(NULL != list)
    {
        r = untar(list->f, list->name);
        fputs("The extraction of ", stderr);
        fputs(list->name, stderr);
        if(r) fputs(" was successful\n", stderr);
        else fputs(" produced errors\n", stderr);
        fclose(list->f);
        list = list->next;
    }

    return 0;
}

File /mescc-tools-extra/ungz.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools-extra/ungz.c'.
URL: https://github.com/oriansj/mescc-tools-extra/blob/460648be66c4b4e4eeb138b8b88d5d6e0077e79e/ungz.c
/* Copyright (C) 2002-2013 Mark Adler, all rights reserved
 * Copyright (C) 2021 Jeremiah Orians
 * This file is part of mescc-tools-extra
 *
 * mescc-tools-extra is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools-extra is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools-extra.  If not, see <http://www.gnu.org/licenses/>.
 */

/* puff.c
 * Copyright (C) 2002-2013 Mark Adler, all rights reserved
 * version 2.3, 21 Jan 2013
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the author be held liable for any damages
 * arising from the use of this software.
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 * Mark Adler    madler@alumni.caltech.edu
 */

/* ungz.c is a gz file decompression utility that leverages puff.c to provide
 * the deflate algorithm with multiple modifications to enable being built by
 * M2-Planet with M2libc.
 *
 *
 * puff.c is a simple inflate written to be an unambiguous way to specify the
 * deflate format.  It is not written for speed but rather simplicity.  As a
 * side benefit, this code might actually be useful when small code is more
 * important than speed, such as bootstrap applications.  For typical deflate
 * data, zlib's inflate() is about four times as fast as puff().  zlib's
 * inflate compiles to around 20K on my machine, whereas puff.c compiles to
 * around 4K on my machine (a PowerPC using GNU cc).  If the faster decode()
 * function here is used, then puff() is only twice as slow as zlib's
 * inflate().
 *
 * All dynamically allocated memory comes from the stack.  The stack required
 * is less than 2K bytes.  This code is compatible with 16-bit int's and
 * assumes that long's are at least 32 bits.  puff.c uses the short data type,
 * assumed to be 16 bits, for arrays in order to conserve memory.  The code
 * works whether integers are stored big endian or little endian.
 *
 * In the comments below are "Format notes" that describe the inflate process
 * and document some of the less obvious aspects of the format.  This source
 * code is meant to supplement RFC 1951, which formally describes the deflate
 * format:
 *
 *    http://www.zlib.org/rfc-deflate.html
 */

/*
 * Change history:
 *
 * 1.0  10 Feb 2002     - First version
 * 1.1  17 Feb 2002     - Clarifications of some comments and notes
 *                      - Update puff() dest and source pointers on negative
 *                        errors to facilitate debugging deflators
 *                      - Remove longest from struct huffman -- not needed
 *                      - Simplify offs[] index in construct()
 *                      - Add input size and checking, using longjmp() to
 *                        maintain easy readability
 *                      - Use short data type for large arrays
 *                      - Use pointers instead of long to specify source and
 *                        destination sizes to avoid arbitrary 4 GB limits
 * 1.2  17 Mar 2002     - Add faster version of decode(), doubles speed (!),
 *                        but leave simple version for readabilty
 *                      - Make sure invalid distances detected if pointers
 *                        are 16 bits
 *                      - Fix fixed codes table error
 *                      - Provide a scanning mode for determining size of
 *                        uncompressed data
 * 1.3  20 Mar 2002     - Go back to lengths for puff() parameters [Gailly]
 *                      - Add a puff.h file for the interface
 *                      - Add braces in puff() for else do [Gailly]
 *                      - Use indexes instead of pointers for readability
 * 1.4  31 Mar 2002     - Simplify construct() code set check
 *                      - Fix some comments
 *                      - Add FIXLCODES #define
 * 1.5   6 Apr 2002     - Minor comment fixes
 * 1.6   7 Aug 2002     - Minor format changes
 * 1.7   3 Mar 2003     - Added test code for distribution
 *                      - Added zlib-like license
 * 1.8   9 Jan 2004     - Added some comments on no distance codes case
 * 1.9  21 Feb 2008     - Fix bug on 16-bit integer architectures [Pohland]
 *                      - Catch missing end-of-block symbol error
 * 2.0  25 Jul 2008     - Add #define to permit distance too far back
 *                      - Add option in TEST code for puff to write the data
 *                      - Add option in TEST code to skip input bytes
 *                      - Allow TEST code to read from piped stdin
 * 2.1   4 Apr 2010     - Avoid variable initialization for happier compilers
 *                      - Avoid unsigned comparisons for even happier compilers
 * 2.2  25 Apr 2010     - Fix bug in variable initializations [Oberhumer]
 *                      - Add const where appropriate [Oberhumer]
 *                      - Split if's and ?'s for coverage testing
 *                      - Break out test code to separate file
 *                      - Move NIL to puff.h
 *                      - Allow incomplete code only if single code length is 1
 *                      - Add full code coverage test to Makefile
 * 2.3  21 Jan 2013     - Check for invalid code length codes in dynamic blocks
 * ??   22 May 2021     - Convert to M2-Planet C subset for bootstrapping purposes.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "M2libc/bootstrappable.h"

/*
 * Maximums for allocations and loops.  It is not useful to change these --
 * they are fixed by the deflate format.
 */
#define MAXBITS 15              /* maximum bits in a code */
#define MAXLCODES 286           /* maximum number of literal/length codes */
#define MAXDCODES 30            /* maximum number of distance codes */
#define MAXCODES 316            /* maximum codes lengths to read (MAXLCODES+MAXDCODES) */
#define FIXLCODES 288           /* number of fixed literal/length codes */

/* input and output state */
struct state {
    /* output state */
    char *out;                  /* output buffer */
    size_t outlen;              /* available space at out */
    size_t outcnt;              /* bytes written to out so far */

    /* input state */
    char *in;                   /* input buffer */
    size_t inlen;               /* available input at in */
    size_t incnt;               /* bytes read so far */
    int bitbuf;                 /* bit buffer */
    int bitcnt;                 /* number of bits in bit buffer */
};

/*
 * Return need bits from the input stream.  This always leaves less than
 * eight bits in the buffer.  bits() works properly for need == 0.
 *
 * Format notes:
 *
 * - Bits are stored in bytes from the least significant bit to the most
 *   significant bit.  Therefore bits are dropped from the bottom of the bit
 *   buffer, using shift right, and new bytes are appended to the top of the
 *   bit buffer, using shift left.
 */
int bits(struct state *s, int need)
{
    long val;           /* bit accumulator (can use up to 20 bits) */
    long hold;

    /* load at least need bits into val */
    val = s->bitbuf;
    while (s->bitcnt < need)
    {
        if (s->incnt == s->inlen)
        {
            fputs("out of input\n", stderr);
            exit(EXIT_FAILURE);
        }
        hold = (s->in[s->incnt] & 0xFF);
        s->incnt = s->incnt + 1;
        val = val | (hold << s->bitcnt);  /* load eight bits */
        s->bitcnt = s->bitcnt + 8;
    }

    /* drop need bits and update buffer, always zero to seven bits left */
    s->bitbuf = (val >> need);
    s->bitcnt = s->bitcnt - need;

    /* return need bits, zeroing the bits above that */
    val = (val & ((1 << need) - 1));
    #if defined(DEBUG)
        fputs(int2str(val, 16, FALSE), stderr);
        fputs(" : bits\n", stderr);
    #endif
    return val;
}

/*
 * Process a stored block.
 *
 * Format notes:
 *
 * - After the two-bit stored block type (00), the stored block length and
 *   stored bytes are byte-aligned for fast copying.  Therefore any leftover
 *   bits in the byte that has the last bit of the type, as many as seven, are
 *   discarded.  The value of the discarded bits are not defined and should not
 *   be checked against any expectation.
 *
 * - The second inverted copy of the stored block length does not have to be
 *   checked, but it's probably a good idea to do so anyway.
 *
 * - A stored block can have zero length.  This is sometimes used to byte-align
 *   subsets of the compressed data for random access or partial recovery.
 */
int stored(struct state *s)
{
    unsigned len;       /* length of stored block */

    /* discard leftover bits from current byte (assumes s->bitcnt < 8) */
    s->bitbuf = 0;
    s->bitcnt = 0;

    /* get length and check against its one's complement */
    if ((s->incnt + 4) > s->inlen) return 2;    /* not enough input */
    len = s->in[s->incnt];
    s->incnt = s->incnt + 1;
    len = len | (s->in[s->incnt] << 8);
    s->incnt = s->incnt + 1;
    if(s->in[s->incnt] != (~len & 0xff)) return -2;                              /* didn't match complement! */
    s->incnt = s->incnt + 1;
    if(s->in[s->incnt] != ((~len >> 8) & 0xff)) return -2;                              /* didn't match complement! */
    s->incnt = s->incnt + 1;

    /* copy len bytes from in to out */
    if ((s->incnt + len) > s->inlen) return 2;                               /* not enough input */
    if (s->out != 0)
    {
        if ((s->outcnt + len) > s->outlen) return 1;                           /* not enough output space */
        while (0 != len)
        {
            len = len - 1;
            s->out[s->outcnt] = s->in[s->incnt];
            s->outcnt = s->outcnt + 1;
            s->incnt = s->incnt + 1;
        }
    }
    else
    {                                      /* just scanning */
        s->outcnt = s->outcnt + len;
        s->incnt = s->incnt + len;
    }

    /* done with a valid stored block */
    return 0;
}

/*
 * Huffman code decoding tables.  count[1..MAXBITS] is the number of symbols of
 * each length, which for a canonical code are stepped through in order.
 * symbol[] are the symbol values in canonical order, where the number of
 * entries is the sum of the counts in count[].  The decoding process can be
 * seen in the function decode() below.
 */
struct huffman
{
    int *count;       /* number of symbols of each length */
    int *symbol;      /* canonically ordered symbols */
};

/*
 * Decode a code from the stream s using huffman table h.  Return the symbol or
 * a negative value if there is an error.  If all of the lengths are zero, i.e.
 * an empty code, or if the code is incomplete and an invalid code is received,
 * then -10 is returned after reading MAXBITS bits.
 *
 * Format notes:
 *
 * - The codes as stored in the compressed data are bit-reversed relative to
 *   a simple integer ordering of codes of the same lengths.  Hence below the
 *   bits are pulled from the compressed data one at a time and used to
 *   build the code value reversed from what is in the stream in order to
 *   permit simple integer comparisons for decoding.  A table-based decoding
 *   scheme (as used in zlib) does not need to do this reversal.
 *
 * - The first code for the shortest length is all zeros.  Subsequent codes of
 *   the same length are simply integer increments of the previous code.  When
 *   moving up a length, a zero bit is appended to the code.  For a complete
 *   code, the last code of the longest length will be all ones.
 *
 * - Incomplete codes are handled by this decoder, since they are permitted
 *   in the deflate format.  See the format notes for fixed() and dynamic().
 */
int decode(struct state *s, struct huffman *h)
{
    int len;            /* current number of bits in code */
    int code = 0;       /* len bits being decoded */
    int first = 0;      /* first code of length len */
    int count;          /* number of codes of length len */
    int index = 0;      /* index of first code of length len in symbol table */
    long hold;

    for (len = 1; len <= MAXBITS; len = len + 1)
    {
        hold = bits(s, 1);              /* get next bit */
        code = code | hold;
        count = h->count[len];
        if ((code - count) < first)
        {
            hold = index + (code - first);
            return h->symbol[hold]; /* if length len, return symbol */
        }
        index = index + count;                 /* else update for next length */
        first = first + count;
        first = first << 1;
        code = code << 1;
    }
    return -10;                         /* ran out of codes */
}

/*
 * Given the list of code lengths length[0..n-1] representing a canonical
 * Huffman code for n symbols, construct the tables required to decode those
 * codes.  Those tables are the number of codes of each length, and the symbols
 * sorted by length, retaining their original order within each length.  The
 * return value is zero for a complete code set, negative for an over-
 * subscribed code set, and positive for an incomplete code set.  The tables
 * can be used if the return value is zero or positive, but they cannot be used
 * if the return value is negative.  If the return value is zero, it is not
 * possible for decode() using that table to return an error--any stream of
 * enough bits will resolve to a symbol.  If the return value is positive, then
 * it is possible for decode() using that table to return an error for received
 * codes past the end of the incomplete lengths.
 *
 * Not used by decode(), but used for error checking, h->count[0] is the number
 * of the n symbols not in the code.  So n - h->count[0] is the number of
 * codes.  This is useful for checking for incomplete codes that have more than
 * one symbol, which is an error in a dynamic block.
 *
 * Assumption: for all i in 0..n-1, 0 <= length[i] <= MAXBITS
 * This is assured by the construction of the length arrays in dynamic() and
 * fixed() and is not verified by construct().
 *
 * Format notes:
 *
 * - Permitted and expected examples of incomplete codes are one of the fixed
 *   codes and any code with a single symbol which in deflate is coded as one
 *   bit instead of zero bits.  See the format notes for fixed() and dynamic().
 *
 * - Within a given code length, the symbols are kept in ascending order for
 *   the code bits definition.
 */
int construct(struct huffman *h, int *length, int n)
{
    int symbol;         /* current symbol when stepping through length[] */
    int len;            /* current length when stepping through h->count[] */
    int left;           /* number of possible codes left of current length */
    int* offs;          /* offsets in symbol table for each length */
    offs = calloc(MAXBITS+1, sizeof(int));
    long hold;

    #if defined(DEBUG)
        int i;
        fputs(int2str(n, 16, FALSE), stderr);
        fputs(" : construct 0\n", stderr);

        for(i = 0; i < n; i = i + 1)
        {
            fputs(int2str(length[i], 16, FALSE), stderr);
            fputs(" : construct 2\n", stderr);
        }
    #endif

    /* count number of codes of each length */
    for (len = 0; len <= MAXBITS; len = len + 1)
    {
        h->count[len] = 0;
    }

    for (symbol = 0; symbol < n; symbol = symbol + 1)
    {
        hold = length[symbol];
        h->count[hold] = h->count[hold] + 1;    /* assumes lengths are within bounds */
    }

    if (h->count[0] == n) return 0;             /* no codes! complete, but decode() will fail */

    /* check for an over-subscribed or incomplete set of lengths */
    left = 1;                                   /* one possible code of zero length */
    for (len = 1; len <= MAXBITS; len = len + 1)
    {
        left = left << 1;                       /* one more bit, double codes left */
        left = left - h->count[len];            /* deduct count from possible codes */
        if (left < 0) return left;              /* over-subscribed--return negative */
    }                                           /* left > 0 means incomplete */

    /* generate offsets into symbol table for each length for sorting */
    offs[1] = 0;
    for (len = 1; len < MAXBITS; len = len + 1)
    {
        offs[len + 1] = offs[len] + h->count[len];
    }

    /*
     * put symbols in table sorted by length, by symbol order within each
     * length
     */
    for (symbol = 0; symbol < n; symbol = symbol + 1)
    {
        if (length[symbol] != 0)
        {
            hold = length[symbol];
            hold = offs[hold];
            h->symbol[hold] = symbol;
            hold = length[symbol];
            offs[hold] = offs[hold] + 1;
        }
    }

    /* return zero for complete set, positive for incomplete set */
    return left;
}

/*
 * Decode literal/length and distance codes until an end-of-block code.
 *
 * Format notes:
 *
 * - Compressed data that is after the block type if fixed or after the code
 *   description if dynamic is a combination of literals and length/distance
 *   pairs terminated by and end-of-block code.  Literals are simply Huffman
 *   coded bytes.  A length/distance pair is a coded length followed by a
 *   coded distance to represent a string that occurs earlier in the
 *   uncompressed data that occurs again at the current location.
 *
 * - Literals, lengths, and the end-of-block code are combined into a single
 *   code of up to 286 symbols.  They are 256 literals (0..255), 29 length
 *   symbols (257..285), and the end-of-block symbol (256).
 *
 * - There are 256 possible lengths (3..258), and so 29 symbols are not enough
 *   to represent all of those.  Lengths 3..10 and 258 are in fact represented
 *   by just a length symbol.  Lengths 11..257 are represented as a symbol and
 *   some number of extra bits that are added as an integer to the base length
 *   of the length symbol.  The number of extra bits is determined by the base
 *   length symbol.  These are in the static arrays below, lens[] for the base
 *   lengths and lext[] for the corresponding number of extra bits.
 *
 * - The reason that 258 gets its own symbol is that the longest length is used
 *   often in highly redundant files.  Note that 258 can also be coded as the
 *   base value 227 plus the maximum extra value of 31.  While a good deflate
 *   should never do this, it is not an error, and should be decoded properly.
 *
 * - If a length is decoded, including its extra bits if any, then it is
 *   followed a distance code.  There are up to 30 distance symbols.  Again
 *   there are many more possible distances (1..32768), so extra bits are added
 *   to a base value represented by the symbol.  The distances 1..4 get their
 *   own symbol, but the rest require extra bits.  The base distances and
 *   corresponding number of extra bits are below in the static arrays dist[]
 *   and dext[].
 *
 * - Literal bytes are simply written to the output.  A length/distance pair is
 *   an instruction to copy previously uncompressed bytes to the output.  The
 *   copy is from distance bytes back in the output stream, copying for length
 *   bytes.
 *
 * - Distances pointing before the beginning of the output data are not
 *   permitted.
 *
 * - Overlapped copies, where the length is greater than the distance, are
 *   allowed and common.  For example, a distance of one and a length of 258
 *   simply copies the last byte 258 times.  A distance of four and a length of
 *   twelve copies the last four bytes three times.  A simple forward copy
 *   ignoring whether the length is greater than the distance or not implements
 *   this correctly.  You should not use memcpy() since its behavior is not
 *   defined for overlapped arrays.  You should not use memmove() or bcopy()
 *   since though their behavior -is- defined for overlapping arrays, it is
 *   defined to do the wrong thing in this case.
 */

int* codes_lens()
{
    /* Size base for length codes 257..285 */
    int* r = calloc(30, sizeof(int));
    r[0] = 3;
    r[1] = 4;
    r[2] = 5;
    r[3] = 6;
    r[4] = 7;
    r[5] = 8;
    r[6] = 9;
    r[7] = 10;
    r[8] = 11;
    r[9] = 13;
    r[10] = 15;
    r[11] = 17;
    r[12] = 19;
    r[13] = 23;
    r[14] = 27;
    r[15] = 31;
    r[16] = 35;
    r[17] = 43;
    r[18] = 51;
    r[19] = 59;
    r[20] = 67;
    r[21] = 83;
    r[22] = 99;
    r[23] = 115;
    r[24] = 131;
    r[25] = 163;
    r[26] = 195;
    r[27] = 227;
    r[28] = 258;
    return r;
}

int* codes_lext()
{
    /* Extra bits for length codes 257..285 */
    int* r = calloc(30, sizeof(int));
    r[0] = 0;
    r[1] = 0;
    r[2] = 0;
    r[3] = 0;
    r[4] = 0;
    r[5] = 0;
    r[6] = 0;
    r[7] = 0;
    r[8] = 1;
    r[9] = 1;
    r[10] = 1;
    r[11] = 1;
    r[12] = 2;
    r[13] = 2;
    r[14] = 2;
    r[15] = 2;
    r[16] = 3;
    r[17] = 3;
    r[18] = 3;
    r[19] = 3;
    r[20] = 4;
    r[21] = 4;
    r[22] = 4;
    r[23] = 4;
    r[24] = 5;
    r[25] = 5;
    r[26] = 5;
    r[27] = 5;
    r[28] = 0;
    return r;
}

int* codes_dists()
{
    /* Offset base for distance codes 0..29 */
    int* r = calloc(31, sizeof(int));
    r[0] = 1;
    r[1] = 2;
    r[2] = 3;
    r[3] = 4;
    r[4] = 5;
    r[5] = 7;
    r[6] = 9;
    r[7] = 13;
    r[8] = 17;
    r[9] = 25;
    r[10] = 33;
    r[11] = 49;
    r[12] = 65;
    r[13] = 97;
    r[14] = 129;
    r[15] = 193;
    r[16] = 257;
    r[17] = 385;
    r[18] = 513;
    r[19] = 769;
    r[20] = 1025;
    r[21] = 1537;
    r[22] = 2049;
    r[23] = 3073;
    r[24] = 4097;
    r[25] = 6145;
    r[26] = 8193;
    r[27] = 12289;
    r[28] = 16385;
    r[29] = 24577;
    return r;
}

int* codes_dext()
{
    /* Extra bits for distance codes 0..29 */
    int* r = calloc(31, sizeof(int));
    r[0] = 0;
    r[1] = 0;
    r[2] = 0;
    r[3] = 0;
    r[4] = 1;
    r[5] = 1;
    r[6] = 2;
    r[7] = 2;
    r[8] = 3;
    r[9] = 3;
    r[10] = 4;
    r[11] = 4;
    r[12] = 5;
    r[13] = 5;
    r[14] = 6;
    r[15] = 6;
    r[16] = 7;
    r[17] = 7;
    r[18] = 8;
    r[19] = 8;
    r[20] = 9;
    r[21] = 9;
    r[22] = 10;
    r[23] = 10;
    r[24] = 11;
    r[25] = 11;
    r[26] = 12;
    r[27] = 12;
    r[28] = 13;
    r[29] = 13;
    return r;
}

int codes(struct state *s, struct huffman *lencode, struct huffman *distcode)
{
    int symbol;         /* decoded symbol */
    int len;            /* length for copy */
    unsigned dist;      /* distance for copy */
    int* lens = codes_lens();
    int* lext = codes_lext();
    int* dists = codes_dists();
    int* dext = codes_dext();

    /* decode literals and length/distance pairs */
    do
    {
        symbol = decode(s, lencode);
        if (symbol < 0) return symbol;          /* invalid symbol */
        if (symbol < 256)                       /* literal: symbol is the byte */
        {
            /* write out the literal */
            if (s->out != 0)
            {
                if (s->outcnt == s->outlen) return 1;
                s->out[s->outcnt] = symbol;
            }
            s->outcnt = s->outcnt + 1;
        }
        else if (symbol > 256)                  /* length */
        {
            /* get and compute length */
            symbol = symbol - 257;
            if (symbol >= 29) return -10;       /* invalid fixed code */
            len = lens[symbol] + bits(s, lext[symbol]);

            /* get and check distance */
            symbol = decode(s, distcode);
            if (symbol < 0) return symbol;      /* invalid symbol */
            dist = dists[symbol] + bits(s, dext[symbol]);
            if (dist > s->outcnt) return -11;   /* distance too far back */

            /* copy length bytes from distance bytes back */
            if (s->out != 0)
            {
                if (s->outcnt + len > s->outlen) return 1;
                while (0 != len)
                {
                    len = len - 1;
                    if(dist > s->outcnt) s->out[s->outcnt] = 0;
                    else s->out[s->outcnt] = s->out[s->outcnt - dist];
                    s->outcnt = s->outcnt + 1;
                }
            }
            else s->outcnt = s->outcnt + len;
        }
    } while (symbol != 256);            /* end of block symbol */

    /* done with a valid fixed or dynamic block */
    return 0;
}

/*
 * Process a fixed codes block.
 *
 * Format notes:
 *
 * - This block type can be useful for compressing small amounts of data for
 *   which the size of the code descriptions in a dynamic block exceeds the
 *   benefit of custom codes for that block.  For fixed codes, no bits are
 *   spent on code descriptions.  Instead the code lengths for literal/length
 *   codes and distance codes are fixed.  The specific lengths for each symbol
 *   can be seen in the "for" loops below.
 *
 * - The literal/length code is complete, but has two symbols that are invalid
 *   and should result in an error if received.  This cannot be implemented
 *   simply as an incomplete code since those two symbols are in the "middle"
 *   of the code.  They are eight bits long and the longest literal/length\
 *   code is nine bits.  Therefore the code must be constructed with those
 *   symbols, and the invalid symbols must be detected after decoding.
 *
 * - The fixed distance codes also have two invalid symbols that should result
 *   in an error if received.  Since all of the distance codes are the same
 *   length, this can be implemented as an incomplete code.  Then the invalid
 *   codes are detected while decoding.
 */
int fixed(struct state *s)
{
    int* lencnt = calloc((MAXBITS + 1), sizeof(int));
    int* lensym = calloc(FIXLCODES, sizeof(int));
    int* distcnt = calloc((MAXBITS + 1), sizeof(int));
    int* distsym = calloc(MAXDCODES, sizeof(int));
    struct huffman* lencode = calloc(1, sizeof(struct huffman));
    struct huffman* distcode = calloc(1, sizeof(struct huffman));
    int hold;

    /* build fixed huffman tables if first call (may not be thread safe) */
    int symbol;
    int* lengths = calloc(FIXLCODES, sizeof(int));

    /* construct lencode and distcode */
    lencode->count = lencnt;
    lencode->symbol = lensym;
    distcode->count = distcnt;
    distcode->symbol = distsym;

    /* literal/length table */
    for (symbol = 0; symbol < 144; symbol = symbol + 1)
    {
        lengths[symbol] = 8;
    }

    while(symbol < 256)
    {
        lengths[symbol] = 9;
        symbol = symbol + 1;
    }

    while(symbol < 280)
    {
        lengths[symbol] = 7;
        symbol = symbol + 1;
    }

    while(symbol < FIXLCODES)
    {
        lengths[symbol] = 8;
        symbol = symbol + 1;
    }

    construct(lencode, lengths, FIXLCODES);

    /* distance table */
    for (symbol = 0; symbol < MAXDCODES; symbol = symbol + 1)
    {
        lengths[symbol] = 5;
    }

    construct(distcode, lengths, MAXDCODES);

    /* decode data until end-of-block code */
    hold = codes(s, lencode, distcode);
    return hold;
}

/*
 * Process a dynamic codes block.
 *
 * Format notes:
 *
 * - A dynamic block starts with a description of the literal/length and
 *   distance codes for that block.  New dynamic blocks allow the compressor to
 *   rapidly adapt to changing data with new codes optimized for that data.
 *
 * - The codes used by the deflate format are "canonical", which means that
 *   the actual bits of the codes are generated in an unambiguous way simply
 *   from the number of bits in each code.  Therefore the code descriptions
 *   are simply a list of code lengths for each symbol.
 *
 * - The code lengths are stored in order for the symbols, so lengths are
 *   provided for each of the literal/length symbols, and for each of the
 *   distance symbols.
 *
 * - If a symbol is not used in the block, this is represented by a zero as
 *   as the code length.  This does not mean a zero-length code, but rather
 *   that no code should be created for this symbol.  There is no way in the
 *   deflate format to represent a zero-length code.
 *
 * - The maximum number of bits in a code is 15, so the possible lengths for
 *   any code are 1..15.
 *
 * - The fact that a length of zero is not permitted for a code has an
 *   interesting consequence.  Normally if only one symbol is used for a given
 *   code, then in fact that code could be represented with zero bits.  However
 *   in deflate, that code has to be at least one bit.  So for example, if
 *   only a single distance base symbol appears in a block, then it will be
 *   represented by a single code of length one, in particular one 0 bit.  This
 *   is an incomplete code, since if a 1 bit is received, it has no meaning,
 *   and should result in an error.  So incomplete distance codes of one symbol
 *   should be permitted, and the receipt of invalid codes should be handled.
 *
 * - It is also possible to have a single literal/length code, but that code
 *   must be the end-of-block code, since every dynamic block has one.  This
 *   is not the most efficient way to create an empty block (an empty fixed
 *   block is fewer bits), but it is allowed by the format.  So incomplete
 *   literal/length codes of one symbol should also be permitted.
 *
 * - If there are only literal codes and no lengths, then there are no distance
 *   codes.  This is represented by one distance code with zero bits.
 *
 * - The list of up to 286 length/literal lengths and up to 30 distance lengths
 *   are themselves compressed using Huffman codes and run-length encoding.  In
 *   the list of code lengths, a 0 symbol means no code, a 1..15 symbol means
 *   that length, and the symbols 16, 17, and 18 are run-length instructions.
 *   Each of 16, 17, and 18 are follwed by extra bits to define the length of
 *   the run.  16 copies the last length 3 to 6 times.  17 represents 3 to 10
 *   zero lengths, and 18 represents 11 to 138 zero lengths.  Unused symbols
 *   are common, hence the special coding for zero lengths.
 *
 * - The symbols for 0..18 are Huffman coded, and so that code must be
 *   described first.  This is simply a sequence of up to 19 three-bit values
 *   representing no code (0) or the code length for that symbol (1..7).
 *
 * - A dynamic block starts with three fixed-size counts from which is computed
 *   the number of literal/length code lengths, the number of distance code
 *   lengths, and the number of code length code lengths (ok, you come up with
 *   a better name!) in the code descriptions.  For the literal/length and
 *   distance codes, lengths after those provided are considered zero, i.e. no
 *   code.  The code length code lengths are received in a permuted order (see
 *   the order[] array below) to make a short code length code length list more
 *   likely.  As it turns out, very short and very long codes are less likely
 *   to be seen in a dynamic code description, hence what may appear initially
 *   to be a peculiar ordering.
 *
 * - Given the number of literal/length code lengths (nlen) and distance code
 *   lengths (ndist), then they are treated as one long list of nlen + ndist
 *   code lengths.  Therefore run-length coding can and often does cross the
 *   boundary between the two sets of lengths.
 *
 * - So to summarize, the code description at the start of a dynamic block is
 *   three counts for the number of code lengths for the literal/length codes,
 *   the distance codes, and the code length codes.  This is followed by the
 *   code length code lengths, three bits each.  This is used to construct the
 *   code length code which is used to read the remainder of the lengths.  Then
 *   the literal/length code lengths and distance lengths are read as a single
 *   set of lengths using the code length codes.  Codes are constructed from
 *   the resulting two sets of lengths, and then finally you can start
 *   decoding actual compressed data in the block.
 *
 * - For reference, a "typical" size for the code description in a dynamic
 *   block is around 80 bytes.
 */

int* dynamic_order()
{
    /* permutation of code length codes */
    int* r = calloc(20, sizeof(int));
    r[0] = 16;
    r[1] = 17;
    r[2] = 18;
    r[3] = 0;
    r[4] = 8;
    r[5] = 7;
    r[6] = 9;
    r[7] = 6;
    r[8] = 10;
    r[9] = 5;
    r[10] = 11;
    r[11] = 4;
    r[12] = 12;
    r[13] = 3;
    r[14] = 13;
    r[15] = 2;
    r[16] = 14;
    r[17] = 1;
    r[18] = 15;
    return r;
}

int dynamic(struct state *s)
{
    #if defined(__M2__)
        int array = sizeof(int);
    #else
        int array = 1;
    #endif

    int nlen;
    int ndist;
    int ncode;                          /* number of lengths in descriptor */
    int index;                          /* index of lengths[] */
    int err;                            /* construct() return value */
    int* lengths = calloc(MAXCODES, sizeof(int));       /* descriptor code lengths */
    int* lencnt = calloc((MAXBITS + 1), sizeof(int));
    int* lensym = calloc(MAXLCODES, sizeof(int));       /* lencode memory */
    int* distcnt = calloc((MAXBITS + 1), sizeof(int));
    int* distsym = calloc(MAXDCODES, sizeof(int));      /* distcode memory */
    struct huffman* lencode = calloc(1, sizeof(struct huffman));
    struct huffman* distcode = calloc(1, sizeof(struct huffman));
    int* order = dynamic_order();
    long hold;
    int* set;

    /* construct lencode and distcode */
    lencode->count = lencnt;
    lencode->symbol = lensym;
    distcode->count = distcnt;
    distcode->symbol = distsym;

    /* get number of lengths in each table, check lengths */
    nlen = bits(s, 5) + 257;
    ndist = bits(s, 5) + 1;
    ncode = bits(s, 4) + 4;
    if (nlen > MAXLCODES) return -3;    /* bad counts */
    if(ndist > MAXDCODES) return -3;    /* bad counts */

    /* read code length code lengths (really), missing lengths are zero */
    for (index = 0; index < ncode; index = index + 1)
    {
        hold = order[index];
        lengths[hold] = bits(s, 3);
    }

    while(index < 19)
    {
        hold = order[index];
        lengths[hold] = 0;
        index = index + 1;
    }

    /* build huffman table for code lengths codes (use lencode temporarily) */
    err = construct(lencode, lengths, 19);
    if (err != 0) return -4;            /* require complete code set here */

    /* read length/literal and distance code length tables */
    index = 0;
    int symbol;                         /* decoded value */
    int len;                            /* last length to repeat */
    while (index < (nlen + ndist))
    {
        symbol = decode(s, lencode);
        if (symbol < 0) return symbol;  /* invalid symbol */

        if (symbol < 16)                /* length in 0..15 */
        {
            lengths[index] = symbol;
            index = index + 1;
        }
        else                            /* repeat instruction */
        {
            len = 0;                    /* assume repeating zeros */
            if (symbol == 16)           /* repeat last length 3..6 times */
            {
                if (index == 0) return -5;      /* no last length! */
                len = lengths[index - 1];       /* last length */
                symbol = 3 + bits(s, 2);
            }
            else if (symbol == 17) symbol = 3 + bits(s, 3); /* repeat zero 3..10 times */
            else symbol = 11 + bits(s, 7);      /* == 18, repeat zero 11..138 times */

            if ((index + symbol) > (nlen + ndist)) return -6;   /* too many lengths! */

            while(0 != symbol)            /* repeat last or zero symbol times */
            {
                lengths[index] = len;
                index = index + 1;
                symbol = symbol - 1;
            }
        }
    }

    /* check for end-of-block code -- there better be one! */
    if (lengths[256] == 0) return -9;

    /* build huffman table for literal/length codes */
    err = construct(lencode, lengths, nlen);

    /* incomplete code ok only for single length 1 code */
    if (err < 0) return -7;
    if((0 != err) && (nlen != (lencode->count[0] + lencode->count[1]))) return -7;

    /* build huffman table for distance codes */
    set = lengths + (nlen * array);
    err = construct(distcode, set, ndist);

    /* incomplete code ok only for single length 1 code */
    if (err < 0) return -8;
    if((0 != err) && (ndist != (distcode->count[0] + distcode->count[1]))) return -8;

    /* decode data until end-of-block code */
    hold = codes(s, lencode, distcode);
    return hold;
}

/*
 * Inflate source to dest.  On return, destlen and sourcelen are updated to the
 * size of the uncompressed data and the size of the deflate data respectively.
 * On success, the return value of puff() is zero.  If there is an error in the
 * source data, i.e. it is not in the deflate format, then a negative value is
 * returned.  If there is not enough input available or there is not enough
 * output space, then a positive error is returned.  In that case, destlen and
 * sourcelen are not updated to facilitate retrying from the beginning with the
 * provision of more input data or more output space.  In the case of invalid
 * inflate data (a negative error), the dest and source pointers are updated to
 * facilitate the debugging of deflators.
 *
 * puff() also has a mode to determine the size of the uncompressed output with
 * no output written.  For this dest must be (unsigned char *)0.  In this case,
 * the input value of *destlen is ignored, and on return *destlen is set to the
 * size of the uncompressed output.
 *
 * The return codes are:
 *
 *   2:  available inflate data did not terminate
 *   1:  output space exhausted before completing inflate
 *   0:  successful inflate
 *  -1:  invalid block type (type == 3)
 *  -2:  stored block length did not match one's complement
 *  -3:  dynamic block code description: too many length or distance codes
 *  -4:  dynamic block code description: code lengths codes incomplete
 *  -5:  dynamic block code description: repeat lengths with no first length
 *  -6:  dynamic block code description: repeat more than specified lengths
 *  -7:  dynamic block code description: invalid literal/length code lengths
 *  -8:  dynamic block code description: invalid distance code lengths
 *  -9:  dynamic block code description: missing end-of-block code
 * -10:  invalid literal/length or distance code in fixed or dynamic block
 * -11:  distance is too far back in fixed or dynamic block
 *
 * Format notes:
 *
 * - Three bits are read for each block to determine the kind of block and
 *   whether or not it is the last block.  Then the block is decoded and the
 *   process repeated if it was not the last block.
 *
 * - The leftover bits in the last byte of the deflate data after the last
 *   block (if it was a fixed or dynamic block) are undefined and have no
 *   expected values to check.
 */

struct puffer
{
    int error;
    size_t destlen;
    size_t sourcelen;
};

struct puffer* puff(char* dest, size_t destlen, char* source, size_t sourcelen)
{
    struct state* s = calloc(1, sizeof(struct state));             /* input/output state */
    int last;
    int type;                   /* block information */
    int err;                    /* return value */

    /* initialize output state */
    s->out = dest;
    s->outlen = destlen;                /* ignored if dest is NIL */
    s->outcnt = 0;

    /* initialize input state */
    s->in = source;
    s->inlen = sourcelen;
    s->incnt = 0;
    s->bitbuf = 0;
    s->bitcnt = 0;

    /* process blocks until last block or error */
    do
    {
        last = bits(s, 1);         /* one if last block */
        type = bits(s, 2);         /* block type 0..3 */

        if(0 == type)
        {
            err = stored(s);
        }
        else if(1 == type)
        {
            err = fixed(s);
        }
        else if(2 == type)
        {
            err = dynamic(s);
        }
        else err = -1;

        if (err != 0) break;                  /* return with error */
    } while (!last);

    /* update the lengths and return */
    struct puffer* r = calloc(1, sizeof(struct puffer));
    r->error = err;
    r->destlen = s->outcnt;
    r->sourcelen = s->incnt;
    return r;
}

void write_blob(char* s, int start, int len, FILE* f)
{
    char* table = "0123456789ABCDEF";
    if(start > len) return;

    int i = s[start] & 0xFF;
    fputc(table[(i >> 4)], f);
    fputc(table[(i & 0xF)], f);
    fputc(' ', f);

    if(start == len) fputc('\n', f);
    else fputc(' ', f);
    write_blob(s, start + 1, len, f);
}

#define FTEXT 0x01
#define FHCRC 0x02
#define FEXTRA 0x04
#define FNAME 0x08
#define FCOMMENT 0x10


struct gz
{
    char* HEADER;
    int ID;
    int CM;
    int FLG;
    int MTIME;
    int XFL;
    int OS;
    int XLEN;
    char* FLG_FEXTRA;
    char* FLG_FNAME;
    char* FLG_FCOMMENT;
    int CRC16;
    char* FLG_FHCRC;
    char* block;
    int CRC32;
    size_t ISIZE;
    size_t file_size;
};

/* Read the input file *name, or stdin if name is NULL, into allocated memory.
   Reallocate to larger buffers until the entire file is read in.  Return a
   pointer to the allocated data, or NULL if there was a memory allocation
   failure.  *len is the number of bytes of data read from the input file (even
   if load() returns NULL).  If the input file was empty or could not be opened
   or read, *len is zero. */
struct gz* load(char* name)
{
    struct gz* r = calloc(1, sizeof(struct gz));
    char* scratch = calloc(5, sizeof(char));
    FILE* f = fopen(name, "r");
    int count;
    int ID1;
    int ID2;
    int count1;
    int count2;
    int count3;
    int count4;
    int c;
    int i;
    char* s = calloc(11, sizeof(char));

    if(NULL == f)
    {
        fputs("unable to open file: ", stderr);
        fputs(name, stderr);
        fputs("\nfor reading\n", stderr);
        return NULL;
    }

    fseek(f, 0, SEEK_END);
    r->file_size = ftell(f);
    fseek(f, 0, SEEK_SET);
    count = fread(s, sizeof(char), 10, f);

    if(10 != count)
    {
        fputs("incomplete gzip header\n", stderr);
        return NULL;
    }

    /* Verify header */
    r->HEADER = s;

    #if defined(DEBUG)
        write_blob(s, 0, 10, stderr);
    #endif

    ID1 = (s[0] & 0xFF);
    ID2 = (s[1] & 0xFF);
    r->ID = ((ID1 << 8) | ID2);
    if(0x1f8b != r->ID)
    {
        fputs("bad header\n", stderr);
        return NULL;
    }

    /* Verify Compression */
    r->CM = (r->HEADER[2] & 0xFF);
    if(8 != r->CM)
    {
        fputs("NOT DEFLATE COMPRESSION\n", stderr);
        return NULL;
    }

    /* Get specials specified in flag bits */
    r->FLG = (r->HEADER[3] & 0xFF);

    if(0 != (FEXTRA & r->FLG))
    {
        count = fread(scratch, sizeof(char), 4, f);
        count1 = (scratch[0] & 0xFF);
        count2 = (scratch[1] & 0xFF);
        count3 = (scratch[2] & 0xFF);
        count4 = (scratch[3] & 0xFF);
        count = (count1 << 24) | (count2 << 16) | (count3 << 8) | count4;
        require(0 < count, "FEXTRA field needs to be a positive number of bytes in size\n");
        require(100000000 > count, "we don't support FEXTRA fields greater than 100MB in size\n");
        r->FLG_FEXTRA = calloc(count + 1, sizeof(char));
        fread(r->FLG_FEXTRA, sizeof(char), count, f);
    }

    if(0 != (FNAME & r->FLG))
    {
        r->FLG_FNAME = calloc(r->file_size, sizeof(char));
        i = 0;
        do
        {
            c = fgetc(f);
            require(0 <= c, "received a non-null terminated filename in the file\n");
            r->FLG_FNAME[i] = c;
            i = i + 1;
        } while(0 != c);
    }

    if(0 != (FCOMMENT & r->FLG))
    {
        r->FLG_FCOMMENT = calloc(r->file_size, sizeof(char));
        i = 0;
        do
        {
            c = fgetc(f);
            require(0 <= c, "received a non-null terminated comment in the file\n");
            r->FLG_FCOMMENT[i] = c;
            i = i + 1;
        } while(0 != c);
    }

    if(0 != (FHCRC & r->FLG))
    {
        /* Not implemented */
        fputs("FHCRC is not implemented at this time\n", stderr);
        return NULL;
    }

    if(NULL == r->FLG_FNAME)
    {
        count = strlen(name) - 3;
        r->FLG_FNAME = calloc(count + 4, sizeof(char));
        i = 0;
        while(i < count)
        {
            r->FLG_FNAME[i] = name[i];
            i = i + 1;
        }
    }

    r->block = calloc(r->file_size, sizeof(char));
    count = fread(r->block, sizeof(char), r->file_size, f);
    r->ISIZE = count;
    fclose(f);
    return r;
}

int main(int argc, char **argv)
{
    struct puffer* ret;
    char* name;
    char* buffer;
    char *dest;
    struct gz* in;
    FILE* out;
    int FUZZING = FALSE;

    /* process arguments */
    int i = 1;
    while (i < argc)
    {
        if(NULL == argv[i])
        {
            i = i + 1;
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            name = argv[i+1];
            require(NULL != name, "the --file option requires a filename to be given\n");
            i = i + 2;
        }
        else if(match(argv[i], "-o") || match(argv[i], "--output"))
        {
            dest = argv[i+1];
            require(NULL != dest, "the --output option requires a filename to be given\n");
            i = i + 2;
        }
        else if(match(argv[i], "--chaos") || match(argv[i], "--fuzz-mode") || match(argv[i], "--fuzzing"))
        {
            FUZZING = TRUE;
            fputs("fuzz-mode enabled, preparing for chaos\n", stderr);
            i = i + 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" --file $input.gz", stderr);
            fputs(" [--output $output] (or it'll use the internal filename)\n", stderr);
            fputs("--help to get this message\n", stderr);
            fputs("--fuzz-mode if you wish to fuzz this application safely\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option:", stderr);
            fputs(argv[i], stderr);
            fputs("\nAborting to avoid problems\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    in = load(name);

    if (in == NULL)
    {
        fputs("memory allocation failure\nDidn't read file\n", stderr);
        exit(1);
    }

    ret = puff(0, 0, in->block, in->ISIZE);

    if(NULL == dest)
    {
        dest = in->FLG_FNAME;
    }

    fputs(name, stderr);
    fputs(" => ", stderr);
    fputs(dest, stderr);

    if (0 != ret->error)
    {
        fputs("puff() failed with return code ", stderr);
        fputs(int2str(ret->error, 10, TRUE), stderr);
        fputc('\n', stderr);
        exit(3);
    }
    else
    {
        fputs(": succeeded uncompressing ", stderr);
        fputs(int2str(ret->destlen, 10, FALSE), stderr);
        fputs(" bytes\n", stderr);
    }

    buffer = malloc(ret->destlen);
    if (buffer == NULL)
    {
        fputs("memory allocation failure\n", stderr);
        return 4;
    }

    ret = puff(buffer, ret->destlen, in->block, in->ISIZE);

    if(!FUZZING)
    {
        out = fopen(dest, "w");
        fwrite(buffer, 1, ret->destlen, out);
    }
    else
    {
        fputs("skipped write to file due to --fuzz-mode flag\n", stderr);
    }
    free(buffer);

    /* clean up */
    return 0;
}

File /mescc-tools-extra/unbz2.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools-extra/unbz2.c'.
URL: https://github.com/oriansj/mescc-tools-extra/blob/460648be66c4b4e4eeb138b8b88d5d6e0077e79e/unbz2.c
/* Copyright (C) 2003, 2007 Rob Landley <rob@landley.net>
 * Copyright (C) 2022 Paul Dersey <pdersey@gmail.com>
 * This file is part of mescc-tools-extra
 *
 * mescc-tools-extra is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools-extra is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools-extra.  If not, see <http://www.gnu.org/licenses/>.
 */

/* bzcat.c - bzip2 decompression
 *
 * Copyright 2003, 2007 Rob Landley <rob@landley.net>
 *
 * Based on a close reading (but not the actual code) of the original bzip2
 * decompression code by Julian R Seward (jseward@acm.org), which also
 * acknowledges contributions by Mike Burrows, David Wheeler, Peter Fenwick,
 * Alistair Moffat, Radford Neal, Ian H. Witten, Robert Sedgewick, and
 * Jon L. Bentley.
 *
 * No standard.
*/

/********************************************************************************
 * unbz2.c is a bz2 file decompression utility based on bzcat.c with            *
 * modifications to enable being built by M2-Planet with M2libc.                *
 ********************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include "M2libc/bootstrappable.h"

// Constants for huffman coding
#define MAX_GROUPS               6
#define GROUP_SIZE               50     /* 64 would have been more efficient */
#define MAX_HUFCODE_BITS         20     /* Longest huffman code allowed */
#define MAX_SYMBOLS              258    /* 256 literals + RUNA + RUNB */
#define SYMBOL_RUNA              0
#define SYMBOL_RUNB              1

// Other housekeeping constants
#define IOBUF_SIZE               4096

// Status return values
#define RETVAL_LAST_BLOCK        (-100)
#define RETVAL_NOT_BZIP_DATA     (-1)
#define RETVAL_DATA_ERROR        (-2)
#define RETVAL_OBSOLETE_INPUT    (-3)

#define INT_MAX 2147483647

// This is what we know about each huffman coding group
struct group_data
{
    int *limit;
    int *base;
    int *permute;
    char minLen;
    char maxLen;
};

// Data for burrows wheeler transform

struct bwdata
{
    unsigned origPtr;
    int *byteCount;
    // State saved when interrupting output
    int writePos;
    int writeRun;
    int writeCount;
    int writeCurrent;
    unsigned dataCRC;
    unsigned headerCRC;
    unsigned *dbuf;
};

// Structure holding all the housekeeping data, including IO buffers and
// memory that persists between calls to bunzip
struct bunzip_data
{
    // Input stream, input buffer, input bit buffer
    int in_fd;
    int inbufCount;
    int inbufPos;
    char *inbuf;
    unsigned inbufBitCount;
    unsigned inbufBits;

    // Output buffer
    char *outbuf;
    int outbufPos;

    unsigned totalCRC;

    // First pass decompression data (Huffman and MTF decoding)
    char *selectors;                  // nSelectors=15 bits
    struct group_data *groups;   // huffman coding tables
    int symTotal;
    int groupCount;
    int nSelectors;
    unsigned *symToByte;
    unsigned *mtfSymbol;

    // The CRC values stored in the block header and calculated from the data
    unsigned *crc32Table;

    // Second pass decompression data (burrows-wheeler transform)
    unsigned dbufSize;
    struct bwdata* bwdata;
};

int FUZZING;


void crc_init(unsigned *crc_table, int little_endian)
{
    unsigned i;
    unsigned j;
    unsigned c;

    // Init the CRC32 table (big endian)
    for(i = 0; i < 256; i += 1)
    {
        if(little_endian)
        {
            c = i;
        }
        else
        {
            c = i << 24;
        }

        for(j = 8; j > 0; j -= 1)
        {
            if(little_endian)
            {
                if(c & 1)
                {
                    c = (c >> 1) ^ 0xEDB88320;
                }
                else
                {
                    c = c >> 1;
                }
            }
            else
            {
                if(c & 0x80000000)
                {
                    c = (c << 1) ^ 0x04C11DB7;
#if defined(__M2__)

                    // & 0xFFFFFFFF not working
                    if(sizeof(unsigned) == 8)
                    {
                        c <<= 32;
                        c >>= 32;
                    }

#endif
                }
                else
                {
                    c = c << 1;
                }
            }
        }

        crc_table[i] = c;
    }
}

// Return the next nnn bits of input.  All reads from the compressed input
// are done through this function.  All reads are big endian.
unsigned get_bits(struct bunzip_data *bd, char bits_wanted)
{
    unsigned bits = 0;

    // If we need to get more data from the byte buffer, do so.  (Loop getting
    // one byte at a time to enforce endianness and avoid unaligned access.)
    while(bd->inbufBitCount < bits_wanted)
    {
        // If we need to read more data from file into byte buffer, do so
        if(bd->inbufPos == bd->inbufCount)
        {
            if(0 >= (bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE)))
            {
                exit(1);
            }

            bd->inbufPos = 0;
        }

        // Avoid 32-bit overflow (dump bit buffer to top of output)
        if(bd->inbufBitCount >= 24)
        {
            bits = bd->inbufBits & ((1 << bd->inbufBitCount) - 1);
            bits_wanted = bits_wanted - bd->inbufBitCount;
            bits = bits << bits_wanted;
            bd->inbufBitCount = 0;
        }

        // Grab next 8 bits of input from buffer.
        bd->inbufBits = (bd->inbufBits << 8) | (bd->inbuf[bd->inbufPos] & 0xFF);
        bd->inbufPos = bd->inbufPos + 1;
        bd->inbufBitCount = bd->inbufBitCount + 8;
    }

    // Calculate result
    bd->inbufBitCount = bd->inbufBitCount - bits_wanted;
    bits = bits | ((bd->inbufBits >> bd->inbufBitCount) & ((1 << bits_wanted) - 1));
    return bits;
}

/* Read block header at start of a new compressed data block.  Consists of:
 *
 * 48 bits : Block signature, either pi (data block) or e (EOF block).
 * 32 bits : bw->headerCRC
 * 1  bit  : obsolete feature flag.
 * 24 bits : origPtr (Burrows-wheeler unwind index, only 20 bits ever used)
 * 16 bits : Mapping table index.
 *[16 bits]: symToByte[symTotal] (Mapping table.  For each bit set in mapping
 *           table index above, read another 16 bits of mapping table data.
 *           If correspondig bit is unset, all bits in that mapping table
 *           section are 0.)
 *  3 bits : groupCount (how many huffman tables used to encode, anywhere
 *           from 2 to MAX_GROUPS)
 * variable: hufGroup[groupCount] (MTF encoded huffman table data.)
 */

int read_block_header(struct bunzip_data *bd, struct bwdata *bw)
{
    struct group_data *hufGroup;
    int hh;
    int ii;
    int jj;
    int kk;
    int symCount;
    int *base;
    int *limit;
    unsigned uc;
    unsigned *length = calloc(MAX_SYMBOLS, sizeof(unsigned));
    unsigned *temp = calloc(MAX_HUFCODE_BITS + 1, sizeof(unsigned));
    size_t minLen;
    size_t maxLen;
    int pp;
#if defined(__M2__)
    int int_array = sizeof(int);
    int group_data_array = sizeof(struct group_data);
#else
    int int_array = 1;
    int group_data_array = 1;
#endif
    size_t hold;
    // Read in header signature and CRC (which is stored big endian)
    ii = get_bits(bd, 24);
    jj = get_bits(bd, 24);
    bw->headerCRC = get_bits(bd, 32);

    // Is this the EOF block with CRC for whole file?  (Constant is "e")
    if(ii == 0x177245 && jj == 0x385090)
    {
        free(length);
        free(temp);
        return RETVAL_LAST_BLOCK;
    }

    // Is this a valid data block?  (Constant is "pi".)
    if(ii != 0x314159 || jj != 0x265359)
    {
        return RETVAL_NOT_BZIP_DATA;
    }

    // We can add support for blockRandomised if anybody complains.
    if(get_bits(bd, 1))
    {
        return RETVAL_OBSOLETE_INPUT;
    }

    if((bw->origPtr = get_bits(bd, 24)) > bd->dbufSize)
    {
        return RETVAL_DATA_ERROR;
    }

    // mapping table: if some byte values are never used (encoding things
    // like ascii text), the compression code removes the gaps to have fewer
    // symbols to deal with, and writes a sparse bitfield indicating which
    // values were present.  We make a translation table to convert the symbols
    // back to the corresponding bytes.
    hh = get_bits(bd, 16);
    bd->symTotal = 0;

    for(ii = 0; ii < 16; ii += 1)
    {
        if(hh & (1 << (15 - ii)))
        {
            kk = get_bits(bd, 16);

            for(jj = 0; jj < 16; jj += 1)
            {
                if(kk & (1 << (15 - jj)))
                {
                    bd->symToByte[bd->symTotal] = (16 * ii) + jj;
                    bd->symTotal += 1;
                }
            }
        }
    }

    // How many different huffman coding groups does this block use?
    bd->groupCount = get_bits(bd, 3);

    if(bd->groupCount < 2 || bd->groupCount > MAX_GROUPS)
    {
        return RETVAL_DATA_ERROR;
    }

    // nSelectors: Every GROUP_SIZE many symbols we switch huffman coding
    // tables.  Each group has a selector, which is an index into the huffman
    // coding table arrays.
    //
    // Read in the group selector array, which is stored as MTF encoded
    // bit runs.  (MTF = Move To Front.  Every time a symbol occurs its moved
    // to the front of the table, so it has a shorter encoding next time.)
    if(!(bd->nSelectors = get_bits(bd, 15)))
    {
        return RETVAL_DATA_ERROR;
    }

    for(ii = 0; ii < bd->groupCount; ii += 1)
    {
        bd->mtfSymbol[ii] = ii;
    }

    for(ii = 0; ii < bd->nSelectors; ii += 1)
    {
        // Get next value
        for(jj = 0; get_bits(bd, 1); jj += 1)
            if(jj >= bd->groupCount)
            {
                return RETVAL_DATA_ERROR;
            }

        // Decode MTF to get the next selector, and move it to the front.
        uc = bd->mtfSymbol[jj];

        while(jj)
        {
            jj = jj - 1;
            bd->mtfSymbol[jj + 1] = bd->mtfSymbol[jj];
        }

        bd->mtfSymbol[0] = bd->selectors[ii] = uc;
    }

    // Read the huffman coding tables for each group, which code for symTotal
    // literal symbols, plus two run symbols (RUNA, RUNB)
    symCount = bd->symTotal + 2;

    for(jj = 0; jj < bd->groupCount; jj += 1)
    {
        // Read lengths
        hh = get_bits(bd, 5);

        for(ii = 0; ii < symCount; ii += 1)
        {
            while(TRUE)
            {
                // !hh || hh > MAX_HUFCODE_BITS in one test.
                if(MAX_HUFCODE_BITS - 1 < hh - 1)
                {
                    return RETVAL_DATA_ERROR;
                }

                // Grab 2 bits instead of 1 (slightly smaller/faster).  Stop if
                // first bit is 0, otherwise second bit says whether to
                // increment or decrement.
                kk = get_bits(bd, 2);

                if(kk & 2)
                {
                    hh += (1 - ((kk & 1) << 1));
                }
                else
                {
                    bd->inbufBitCount += 1;
                    break;
                }
            }

            length[ii] = hh;
        }

        // Find largest and smallest lengths in this group
        minLen = maxLen = length[0];

        for(ii = 1; ii < symCount; ii += 1)
        {
            hold = length[ii];
            if(hold > maxLen)
            {
                maxLen = hold;
            }
            else if(hold < minLen)
            {
                minLen = hold;
            }
        }

        /* Calculate permute[], base[], and limit[] tables from length[].
         *
         * permute[] is the lookup table for converting huffman coded symbols
         * into decoded symbols.  It contains symbol values sorted by length.
         *
         * base[] is the amount to subtract from the value of a huffman symbol
         * of a given length when using permute[].
         *
         * limit[] indicates the largest numerical value a symbol with a given
         * number of bits can have.  It lets us know when to stop reading.
         *
         * To use these, keep reading bits until value <= limit[bitcount] or
         * youve read over 20 bits (error).  Then the decoded symbol
         * equals permute[hufcode_value - base[hufcode_bitcount]].
         */
        hufGroup = bd->groups + (group_data_array * jj);
        require(minLen > 0, "hufGroup minLen can't have negative values\n");
        require(minLen <= MAX_HUFCODE_BITS, "hufGroup minLen can't exceed MAX_HUFCODE_BITS\n");
        hufGroup->minLen = minLen;
        require(maxLen > 0, "hufGroup maxLen can't have negative values\n");
        require(maxLen <= MAX_HUFCODE_BITS, "hufGroup maxLen can't exceed MAX_HUFCODE_BITS\n");
        hufGroup->maxLen = maxLen;
        // Note that minLen cant be smaller than 1, so we adjust the base
        // and limit array pointers so were not always wasting the first
        // entry.  We do this again when using them (during symbol decoding).
        base = hufGroup->base - (int_array * 1);
        require(0 <= base, "can't have a negative hufGroup->base\n");
        limit = hufGroup->limit - (int_array * 1);
        // zero temp[] and limit[], and calculate permute[]
        pp = 0;

        for(ii = minLen; ii <= maxLen; ii += 1)
        {
            require(MAX_HUFCODE_BITS >= ii, "Invalid HUFCODE_BITS length\n");
            temp[ii] = 0;
            limit[ii] = 0;

            for(hh = 0; hh < symCount; hh += 1)
            {
                if(length[hh] == ii)
                {
                    require(MAX_SYMBOLS >= pp, "pp exceeded MAX_SYMBOLS\n");
                    hufGroup->permute[pp] = hh;
                    pp += 1;
                }
            }
        }

        // Count symbols coded for at each bit length
        for(ii = 0; ii < symCount; ii += 1)
        {
            hold = length[ii];
            require(MAX_HUFCODE_BITS >= hold, "Invalid HUFCODE_BITS length\n");
            temp[hold] += 1;
        }

        /* Calculate limit[] (the largest symbol-coding value at each bit
         * length, which is (previous limit<<1)+symbols at this level), and
         * base[] (number of symbols to ignore at each bit length, which is
         * limit minus the cumulative count of symbols coded for already). */
        pp = hh = 0;

        for(ii = minLen; ii < maxLen; ii += 1)
        {
            pp += temp[ii];
            limit[ii] = pp - 1;
            pp = pp << 1;
            hh += temp[ii];
            base[ii + 1] = pp - hh;
        }

        limit[maxLen] = pp + temp[maxLen] - 1;
        limit[maxLen + 1] = INT_MAX;
        base[minLen] = 0;
    }

    free(length);
    free(temp);
    return 0;
}

/* First pass, read blocks symbols into dbuf[dbufCount].
 *
 * This undoes three types of compression: huffman coding, run length encoding,
 * and move to front encoding.  We have to undo all those to know when weve
 * read enough input.
 */

int read_huffman_data(struct bunzip_data *bd, struct bwdata *bw)
{
    struct group_data *hufGroup;
    int ii;
    int jj;
    int kk;
    int runPos;
    int dbufCount;
    int symCount;
    int selector;
    int nextSym;
    int *byteCount;
    int *base;
    int *limit;
    unsigned hh;
    unsigned *dbuf = bw->dbuf;
    unsigned uc;
#if defined(__M2__)
    int int_array = sizeof(int);
    int group_data_array = sizeof(struct group_data);
#else
    int int_array = 1;
    int group_data_array = 1;
#endif
    // Weve finished reading and digesting the block header.  Now read this
    // blocks huffman coded symbols from the file and undo the huffman coding
    // and run length encoding, saving the result into dbuf[dbufCount++] = uc
    // Initialize symbol occurrence counters and symbol mtf table
    byteCount = bw->byteCount;

    for(ii = 0; ii < 256; ii += 1)
    {
        byteCount[ii] = 0;
        bd->mtfSymbol[ii] = ii;
    }

    // Loop through compressed symbols.  This is the first "tight inner loop"
    // that needs to be micro-optimized for speed.  (This one fills out dbuf[]
    // linearly, staying in cache more, so isnt as limited by DRAM access.)
    runPos = 0;
    dbufCount = 0;
    symCount = 0;
    selector = 0;
    // Some unnecessary initializations to shut gcc up.
    base = 0;
    limit = 0;
    hufGroup = 0;
    hh = 0;

    while(TRUE)
    {
        // Have we reached the end of this huffman group?
        if(!(symCount))
        {
            // Determine which huffman coding group to use.
            symCount = GROUP_SIZE - 1;

            if(selector >= bd->nSelectors)
            {
                return RETVAL_DATA_ERROR;
            }

            hufGroup = bd->groups + (group_data_array * bd->selectors[selector]);
            selector += 1;
            base = hufGroup->base - (int_array * 1);
            require(0 <= base, "can't have negative hufGroup->base\n");
            limit = hufGroup->limit - (int_array * 1);
        }
        else
        {
            symCount -= 1;
        }

        // Read next huffman-coded symbol (into jj).
        ii = hufGroup->minLen;
        jj = get_bits(bd, ii);

        while(jj > limit[ii])
        {
            // if (ii > hufGroup->maxLen) return RETVAL_DATA_ERROR;
            ii += 1;

            // Unroll get_bits() to avoid a function call when the datas in
            // the buffer already.
            if(bd->inbufBitCount)
            {
                bd->inbufBitCount -= 1;
                kk = (bd->inbufBits >> bd->inbufBitCount) & 1;
            }
            else
            {
                kk = get_bits(bd, 1);
            }

            jj = (jj << 1) | kk;
        }

        // Huffman decode jj into nextSym (with bounds checking)
        jj -= base[ii];

        if(ii > hufGroup->maxLen || jj >= MAX_SYMBOLS)
        {
            return RETVAL_DATA_ERROR;
        }

        nextSym = hufGroup->permute[jj];

        // If this is a repeated run, loop collecting data
        if(nextSym <= SYMBOL_RUNB)
        {
            // If this is the start of a new run, zero out counter
            if(!runPos)
            {
                runPos = 1;
                hh = 0;
            }

            /* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at
               each bit position, add 1 or 2 instead. For example,
               1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2.
               You can make any bit pattern that way using 1 less symbol than
               the basic or 0/1 method (except all bits 0, which would use no
               symbols, but a run of length 0 doesnt mean anything in this
               context). Thus space is saved. */
            hh += (runPos << nextSym); // +runPos if RUNA; +2*runPos if RUNB
            runPos = runPos << 1;
            continue;
        }

        /* When we hit the first non-run symbol after a run, we now know
           how many times to repeat the last literal, so append that many
           copies to our buffer of decoded symbols (dbuf) now. (The last
           literal used is the one at the head of the mtfSymbol array.) */
        if(runPos)
        {
            runPos = 0;

            // Check for integer overflow
            if(hh > bd->dbufSize || dbufCount + hh > bd->dbufSize)
            {
                return RETVAL_DATA_ERROR;
            }

            uc = bd->symToByte[bd->mtfSymbol[0]];
            byteCount[uc] += hh;

            while(hh)
            {
                hh -= 1;
                dbuf[dbufCount] = uc;
                dbufCount += 1;
            }
        }

        // Is this the terminating symbol?
        if(nextSym > bd->symTotal)
        {
            break;
        }

        /* At this point, the symbol we just decoded indicates a new literal
           character. Subtract one to get the position in the MTF array
           at which this literal is currently to be found. (Note that the
           result cant be -1 or 0, because 0 and 1 are RUNA and RUNB.
           Another instance of the first symbol in the mtf array, position 0,
           would have been handled as part of a run.) */
        if(dbufCount >= bd->dbufSize)
        {
            return RETVAL_DATA_ERROR;
        }

        ii = nextSym - 1;
        uc = bd->mtfSymbol[ii];

        // On my laptop, unrolling this memmove() into a loop shaves 3.5% off
        // the total running time.
        while(ii)
        {
            ii -= 1;
            bd->mtfSymbol[ii + 1] = bd->mtfSymbol[ii];
        }

        bd->mtfSymbol[0] = uc;
        uc = bd->symToByte[uc];
        // We have our literal byte.  Save it into dbuf.
        byteCount[uc] += 1;
        dbuf[dbufCount] = uc;
        dbufCount += 1;
    }

    // Now we know what dbufCount is, do a better sanity check on origPtr.
    if(bw->origPtr >= (bw->writeCount = dbufCount))
    {
        return RETVAL_DATA_ERROR;
    }

    return 0;
}

// Flush output buffer to disk
void flush_bunzip_outbuf(struct bunzip_data *bd, int out_fd)
{
    if(bd->outbufPos)
    {
        if(write(out_fd, bd->outbuf, bd->outbufPos) != bd->outbufPos)
        {
            exit(1);
        }

        bd->outbufPos = 0;
    }
}

void burrows_wheeler_prep(struct bunzip_data *bd, struct bwdata *bw)
{
    int ii;
    int jj;
    int kk;
    unsigned *dbuf = bw->dbuf;
    int *byteCount = bw->byteCount;
    unsigned uc;
    // Turn byteCount into cumulative occurrence counts of 0 to n-1.
    jj = 0;

    for(ii = 0; ii < 256; ii += 1)
    {
        kk = jj + byteCount[ii];
        byteCount[ii] = jj;
        jj = kk;
    }

    // Use occurrence counts to quickly figure out what order dbuf would be in
    // if we sorted it.
    for(ii = 0; ii < bw->writeCount; ii += 1)
    {
        uc = dbuf[ii] & 0xFF;
        dbuf[byteCount[uc]] = dbuf[byteCount[uc]] | (ii << 8);
        byteCount[uc] += 1;
    }

    // blockRandomised support would go here.
    // Using ii as position, jj as previous character, hh as current character,
    // and uc as run count.
    bw->dataCRC = 0xffffffff;

    /* Decode first byte by hand to initialize "previous" byte. Note that it
       doesnt get output, and if the first three characters are identical
       it doesnt qualify as a run (hence uc=255, which will either wrap
       to 1 or get reset). */
    if(bw->writeCount)
    {
        bw->writePos = dbuf[bw->origPtr];
        bw->writeCurrent = bw->writePos;
        bw->writePos = bw->writePos >> 8;
        bw->writeRun = -1;
    }
}

// Decompress a block of text to intermediate buffer
int read_bunzip_data(struct bunzip_data *bd)
{
    int rc = read_block_header(bd, bd->bwdata);

    if(!rc)
    {
        rc = read_huffman_data(bd, bd->bwdata);
    }

    // First thing that can be done by a background thread.
    burrows_wheeler_prep(bd, bd->bwdata);
    return rc;
}

// Undo burrows-wheeler transform on intermediate buffer to produce output.
// If !len, write up to len bytes of data to buf.  Otherwise write to out_fd.
// Returns len ? bytes written : 0.  Notice all errors are negative #s.
//
// Burrows-wheeler transform is described at:
// http://dogma.net/markn/articles/bwt/bwt.htm
// http://marknelson.us/1996/09/01/bwt/

int write_bunzip_data(struct bunzip_data *bd, struct bwdata *bw,
                      int out_fd, char *outbuf, int len)
{
    unsigned *dbuf = bw->dbuf;
    int count;
    int pos;
    int current;
    int run;
    int copies;
    int outbyte;
    int previous;
    int gotcount = 0;
    int i;
    int crc_index;

    while(TRUE)
    {
        // If last read was short due to end of file, return last block now
        if(bw->writeCount < 0)
        {
            return bw->writeCount;
        }

        // If we need to refill dbuf, do it.
        if(!bw->writeCount)
        {
            i = read_bunzip_data(bd);

            if(i)
            {
                if(i == RETVAL_LAST_BLOCK)
                {
                    bw->writeCount = i;
                    return gotcount;
                }
                else
                {
                    return i;
                }
            }
        }

        // loop generating output
        count = bw->writeCount;
        pos = bw->writePos;
        current = bw->writeCurrent;
        run = bw->writeRun;

        while(count)
        {
            // If somebody (like tar) wants a certain number of bytes of
            // data from memory instead of written to a file, humor them.
            if(len && bd->outbufPos >= len)
            {
                goto dataus_interruptus;
            }

            count -= 1;
            // Follow sequence vector to undo Burrows-Wheeler transform.
            previous = current;
            pos = dbuf[pos];
            current = pos & 0xff;
            pos = pos >> 8;

            // Whenever we see 3 consecutive copies of the same byte,
            // the 4th is a repeat count
            if(run == 3)
            {
                run += 1;
                copies = current;
                outbyte = previous;
                current = -1;
            }
            else
            {
                run += 1;
                copies = 1;
                outbyte = current;
            }

            // Output bytes to buffer, flushing to file if necessary
            while(copies)
            {
                copies -= 1;

                if(bd->outbufPos == IOBUF_SIZE)
                {
                    flush_bunzip_outbuf(bd, out_fd);
                }

                bd->outbuf[bd->outbufPos] = outbyte;
                bd->outbufPos += 1;
                crc_index = ((bw->dataCRC >> 24) ^ outbyte) & 0xFF;
                bw->dataCRC = (bw->dataCRC << 8) ^ bd->crc32Table[crc_index];
            }

            if(current != previous)
            {
                run = 0;
            }
        }

        // decompression of this block completed successfully
        bw->dataCRC = ~(bw->dataCRC);
#if defined(__M2__)

        // & 0xFFFFFFFF not working
        if(sizeof(unsigned) == 8)
        {
            bw->dataCRC <<= 32;
            bw->dataCRC >>= 32;
        }

#endif
        bd->totalCRC = ((bd->totalCRC << 1) | (bd->totalCRC >> 31)) ^ bw->dataCRC;

        // if this block had a crc error, force file level crc error.
        if(bw->dataCRC != bw->headerCRC)
        {
            bd->totalCRC = bw->headerCRC + 1;
            return RETVAL_LAST_BLOCK;
        }

dataus_interruptus:
        bw->writeCount = count;

        if(len)
        {
            gotcount += bd->outbufPos;
            memcpy(outbuf, bd->outbuf, len);
            // If we got enough data, checkpoint loop state and return
            len -= bd->outbufPos;

            if(len < 1)
            {
                bd->outbufPos -= len;

                if(bd->outbufPos)
                {
                    memmove(bd->outbuf, bd->outbuf + len, bd->outbufPos);
                }

                bw->writePos = pos;
                bw->writeCurrent = current;
                bw->writeRun = run;
                return gotcount;
            }
        }
    }
}

// Allocate the structure, read file header. If !len, src_fd contains
// filehandle to read from. Else inbuf contains data.
int start_bunzip(struct bunzip_data **bdp, int src_fd)
{
    struct bunzip_data *bd;
    unsigned i;
    // Figure out how much data to allocate.
    i = sizeof(struct bunzip_data);
    // Allocate bunzip_data. Most fields initialize to zero.
    *bdp = malloc(i);
    bd = *bdp;
    memset(bd, 0, i);
    bd->inbuf = calloc(IOBUF_SIZE, sizeof(char));
    bd->outbuf = calloc(IOBUF_SIZE, sizeof(char));
    bd->selectors = calloc(32768, sizeof(char));
    bd->groups = calloc(MAX_GROUPS, sizeof(struct group_data));

    for(i = 0; i < MAX_GROUPS; i += 1)
    {
        bd->groups[i].limit = calloc(MAX_HUFCODE_BITS + 1, sizeof(int));
        bd->groups[i].base = calloc(MAX_HUFCODE_BITS, sizeof(int));
        bd->groups[i].permute = calloc(MAX_SYMBOLS, sizeof(int));
    }

    bd->symToByte = calloc(256, sizeof(unsigned));
    bd->mtfSymbol = calloc(256, sizeof(unsigned));
    bd->crc32Table = calloc(256, sizeof(unsigned));
    bd->bwdata = calloc(1, sizeof(struct bwdata));
    bd->bwdata->byteCount = calloc(256, sizeof(int));
    unsigned *crc32Table;
    bd->in_fd = src_fd;
    crc_init(bd->crc32Table, 0);
    // Ensure that file starts with "BZh".
    char *header = "BZh";

    for(i = 0; i < 3; i += 1) if(get_bits(bd, 8) != header[i])
        {
            return RETVAL_NOT_BZIP_DATA;
        }

    // Next byte ascii 1-9, indicates block size in units of 100k of
    // uncompressed data. Allocate intermediate buffer for block.
    i = get_bits(bd, 8);

    if(i < 49 || i > 57)
    {
        return RETVAL_NOT_BZIP_DATA;
    }

    bd->dbufSize = 100000 * (i - 48);
    bd->bwdata[0].dbuf = malloc(bd->dbufSize * sizeof(int));
    return 0;
}

// Example usage: decompress src_fd to dst_fd. (Stops at end of bzip data,
// not end of file.)
int bunzipStream(int src_fd, int dst_fd)
{
    struct bunzip_data *bd;
    int i;
    int j;

    if(!(i = start_bunzip(&bd, src_fd)))
    {
        i = write_bunzip_data(bd, bd->bwdata, dst_fd, 0, 0);

        if(i == RETVAL_LAST_BLOCK)
        {
            if(bd->bwdata[0].headerCRC == bd->totalCRC)
            {
                i = 0;
            }
            else
            {
                i = RETVAL_DATA_ERROR;
            }
        }
    }

    flush_bunzip_outbuf(bd, dst_fd);
    free(bd->bwdata[0].dbuf);
    free(bd->inbuf);
    free(bd->outbuf);
    free(bd->selectors);

    for(j = 0; j < MAX_GROUPS; j += 1)
    {
        free(bd->groups[j].limit);
        free(bd->groups[j].base);
        free(bd->groups[j].permute);
    }

    free(bd->groups);
    free(bd->symToByte);
    free(bd->mtfSymbol);
    free(bd->crc32Table);
    free(bd->bwdata->byteCount);
    free(bd->bwdata);
    free(bd);
    return -i;
}

void do_bunzip2(int in_fd, int out_fd)
{
    int err = bunzipStream(in_fd, out_fd);

    if(err)
    {
        exit(1);
    }
}

int main(int argc, char **argv)
{
    char *name = NULL;
    char *dest = NULL;
    FUZZING = FALSE;

    /* process arguments */
    int i = 1;

    while(i < argc)
    {
        if(NULL == argv[i])
        {
            i += 1;
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            name = argv[i + 1];
            require(NULL != name, "the --file option requires a filename to be given\n");
            i += 2;
        }
        else if(match(argv[i], "-o") || match(argv[i], "--output"))
        {
            dest = argv[i + 1];
            require(NULL != dest, "the --output option requires a filename to be given\n");
            i += 2;
        }
        else if(match(argv[i], "--fuzzing-mode"))
        {
            FUZZING = TRUE;
            i += 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" --file $input.bz2", stderr);
            fputs(" --output $output\n", stderr);
            fputs("--help to get this message\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option:", stderr);
            fputs(argv[i], stderr);
            fputs("\nAborting to avoid problems\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    /* Deal with no input */
    if(NULL == name)
    {
        fputs("an input file (--file $name) must be provided\n", stderr);
        exit(EXIT_FAILURE);
    }

    int in_fd = open(name, 0, 0);

    if(in_fd < 0)
    {
        fputs("Unable to open input file\n", stderr);
        exit(EXIT_FAILURE);
    }

    /* If an output name isn't provided */
    if(NULL == dest)
    {
        int length = strlen(name);
        require(length > 4, "file name length not sufficient, please provide output name with --output $filename\n");
        /* Assume they want the output file name to be the input file name minus the .bz2 */
        dest = calloc(length, sizeof(char));
        require(NULL != dest, "Failed to allocate new output file name\n");
        /* do name.bz2 => name */
        strcpy(dest, name);
        dest[length-3] = 0;
    }

    int out_fd;
    if(FUZZING)
    {
        /* Dump to /dev/null the garbage data produced during fuzzing */
        out_fd = open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0600);
    }
    else
    {
        out_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, 0600);
    }

    if(out_fd < 0)
    {
        fputs("Unable to open output file for writing\n", stderr);
        exit(EXIT_FAILURE);
    }

    do_bunzip2(in_fd, out_fd);
    close(in_fd);
    close(out_fd);
    exit(0);
}

File /mescc-tools-extra/unxz.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools-extra/unxz.c'.
URL: https://github.com/oriansj/mescc-tools-extra/blob/460648be66c4b4e4eeb138b8b88d5d6e0077e79e/unxz.c
/* Copyright (C) 2019 pts@fazekas.hu
 * Copyright (C) 2024 Jeremiah Orians
 * Copyright (C) 2024 Gábor Stefanik
 * This file is part of mescc-tools-extra
 *
 * mescc-tools-extra is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools-extra is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools-extra.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Built upon the great work in:
 * muxzcat.c: tiny .xz and .lzma decompression filter
 * by pts@fazekas.hu at Wed Jan 30 15:15:23 CET 2019
 * from https://github.com/pts/muxzcat
 * For .xz it supports only LZMA2 (no other filters such as BCJ).
 * For .lzma it doesn't work with files with 5 <= lc + lp <= 12.
 * It doesn't verify checksums (e.g. CRC-32 and CRC-64).
 * It extracts the first stream only, and it ignores the index.
 *
 * LZMA algorithm implementation based on
 * https://github.com/pts/pts-tiny-7z-sfx/commit/b9a101b076672879f861d472665afaa6caa6fec1
 * , which is based on 7z922.tar.bz2.
 */

#include <stdio.h>
#include <string.h>  /* memcpy(), memmove() */
#include <unistd.h>  /* read(), write() */
#include <stdint.h>
#include <stdlib.h>  /* realloc() */
#include "M2libc/bootstrappable.h"

/* Constants needed */
#define SZ_OK 0
#define SZ_ERROR_DATA 1
#define SZ_ERROR_MEM 2  /* Out of memory. */
#define SZ_ERROR_CRC 3
#define SZ_ERROR_UNSUPPORTED 4
#define SZ_ERROR_PARAM 5
#define SZ_ERROR_INPUT_EOF 6
/*#define SZ_ERROR_OUTPUT_EOF 7*/
#define SZ_ERROR_READ 8
#define SZ_ERROR_WRITE 9
#define SZ_ERROR_FINISHED_WITH_MARK 15            /* LzmaDec_DecodeToDic stream was finished with end mark. */
#define SZ_ERROR_NOT_FINISHED 16                  /* LzmaDec_DecodeToDic stream was not finished, i.e. dicfLimit reached while there is input to decompress */
#define SZ_ERROR_NEEDS_MORE_INPUT 17              /* LzmaDec_DecodeToDic, you must provide more input bytes */
/*#define SZ_MAYBE_FINISHED_WITHOUT_MARK SZ_OK*/  /* LzmaDec_DecodeToDic, there is probability that stream was finished without end mark */
#define SZ_ERROR_CHUNK_NOT_CONSUMED 18
#define SZ_ERROR_NEEDS_MORE_INPUT_PARTIAL 17      /* LzmaDec_DecodeToDic, more input needed, but existing input was partially processed */
#define LZMA_REQUIRED_INPUT_MAX 20
#define LZMA_BASE_SIZE 1846
#define LZMA_LIT_SIZE 768
#define LZMA2_LCLP_MAX 4
#define MAX_DIC_SIZE 1610612736  /* ~1.61 GB. 2 GiB is user virtual memory limit for many 32-bit systems. */
#define MAX_DIC_SIZE_PROP 37
#define MAX_MATCH_SIZE 273
#define kNumTopBits 24
#define kTopValue (1 << kNumTopBits)
#define kNumBitModelTotalBits 11
#define kBitModelTotal (1 << kNumBitModelTotalBits)
#define kNumMoveBits 5
#define RC_INIT_SIZE 5
#define kNumPosBitsMax 4
#define kNumPosStatesMax (1 << kNumPosBitsMax)
#define kLenNumLowBits 3
#define kLenNumLowSymbols (1 << kLenNumLowBits)
#define kLenNumMidBits 3
#define kLenNumMidSymbols (1 << kLenNumMidBits)
#define kLenNumHighBits 8
#define kLenNumHighSymbols (1 << kLenNumHighBits)
#define LenChoice 0
#define LenChoice2 (LenChoice + 1)
#define LenLow (LenChoice2 + 1)
#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
#define kNumLenProbs (LenHigh + kLenNumHighSymbols)
#define kNumStates 12
#define kNumLitStates 7
#define kStartPosModelIndex 4
#define kEndPosModelIndex 14
#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
#define kNumPosSlotBits 6
#define kNumLenToPosStates 4
#define kNumAlignBits 4
#define kAlignTableSize (1 << kNumAlignBits)
#define kMatchMinLen 2
#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols)
#define IsMatch 0
#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
#define IsRepG0 (IsRep + kNumStates)
#define IsRepG1 (IsRepG0 + kNumStates)
#define IsRepG2 (IsRepG1 + kNumStates)
#define IsRep0Long (IsRepG2 + kNumStates)
#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
#define LenCoder (Align + kAlignTableSize)
#define RepLenCoder (LenCoder + kNumLenProbs)
#define Literal (RepLenCoder + kNumLenProbs)
#define LZMA_DIC_MIN (1 << 12)
#define SZ_ERROR_BAD_MAGIC 51
#define SZ_ERROR_BAD_STREAM_FLAGS 52  /* SZ_ERROR_BAD_MAGIC is reported instead. */
#define SZ_ERROR_UNSUPPORTED_FILTER_COUNT 53
#define SZ_ERROR_BAD_BLOCK_FLAGS 54
#define SZ_ERROR_UNSUPPORTED_FILTER_ID 55
#define SZ_ERROR_UNSUPPORTED_FILTER_PROPERTIES_SIZE 56
#define SZ_ERROR_BAD_PADDING 57
#define SZ_ERROR_BLOCK_HEADER_TOO_LONG 58
#define SZ_ERROR_BAD_CHUNK_CONTROL_BYTE 59
#define SZ_ERROR_BAD_CHECKSUM_TYPE 60
#define SZ_ERROR_BAD_DICTIONARY_SIZE 61
#define SZ_ERROR_UNSUPPORTED_DICTIONARY_SIZE 62
#define SZ_ERROR_FEED_CHUNK 63
/*#define SZ_ERROR_NOT_FINISHED_WITH_MARK 64*/
#define SZ_ERROR_BAD_DICPOS 65
#define SZ_ERROR_MISSING_INITPROP 67
#define SZ_ERROR_BAD_LCLPPB_PROP 68
#define FILTER_ID_LZMA2 0x21
// 65536 + 12 * 1 byte (sizeof(uint8_t)
#define sizeof_readBuf 65548
#define sizeof_writeBuf 0x1000000
#define MAX_DICF_SIZE (MAX_DIC_SIZE + MAX_MATCH_SIZE + sizeof_writeBuf)  /* Maximum number of bytes in global.dicf. */
#define DUMMY_ERROR 0 /* unexpected end of input stream */
#define DUMMY_LIT 1
#define DUMMY_MATCH 2
#define DUMMY_REP 3
/* (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << LZMA2_LCLP_MAX)) */
#define probs_size 14134
#define BIT31 (1<<31)
#define BITS32 (0x7FFFFFFF | BIT31)
#define HIGHBITS (0xFFFFFFFF - BITS32)

FILE* destination;
FILE* source;
uint32_t pos;

/* For LZMA streams, lc <= 8, lp <= 4, lc + lp <= 8 + 4 == 12.
 * For LZMA2 streams, lc + lp <= 4.
 * Minimum value: 1846.
 * Maximum value for LZMA streams: 1846 + (768 << (8 + 4)) == 3147574.
 * Maximum value for LZMA2 streams: 1846 + (768 << 4) == 14134.
 * Memory usage of prob: sizeof(uint32_t) * value == (2 or 4) * value bytes.
 */

struct CLzmaDec
{
    /* lc, lp and pb would fit into a byte, but i386 code is shorter as uint32_t.
     *
     * Constraints:
     *
     * * (0 <= lc <= 8) by LZMA.
     * * 0 <= lc <= 4 by LZMA2 and muxzcat-LZMA and muzxcat-LZMA2.
     * * 0 <= lp <= 4.
     * * 0 <= pb <= 4.
     * * (0 <= lc + lp == 8 + 4 <= 12) by LZMA.
     * * 0 <= lc + lp <= 4 by LZMA2 and muxzcat-LZMA and muxzcat-LZMA2.
     */
    uint32_t lc;
    uint32_t lp;
    uint32_t pb; /* Configured in prop byte. */
    /* Maximum lookback delta.
     * More optimized implementations (but not this version of muxzcat) need
     * that many bytes of storage for the dictionary. muxzcat uses more,
     * because it keeps the entire decompression output in memory, for
     * the simplicity of the implementation.
     * Configured in dicSizeProp byte. Maximum LZMA and LZMA2 supports is 0xffffffff,
     * maximum we support is MAX_DIC_SIZE == 1610612736.
     */
    uint32_t dicSize;
    uint8_t *buf;
    uint32_t range;
    uint32_t code;
    uint32_t dicfPos;  /* The next decompression output byte will be written to dicf + dicfPos. */
    uint32_t dicfLimit;  /* It's OK to write this many decompression output bytes to dic. GrowDic(dicfPos + len) must be called before writing len bytes at dicfPos. */
    uint32_t writtenPos;  /* Decompression output bytes dicf[:writtenPos] are already written to the output file. writtenPos <= dicfPos. */
    uint32_t discardedSize;  /* Number of decompression output bytes discarded. */
    uint32_t writeRemaining;  /* Maximum number of remaining bytes to write, or ~0 for unlimited. */
    uint32_t allocCapacity;  /* Number of bytes allocated in dic. */
    uint32_t processedPos;  /* Decompression output byte count since the last call to LzmaDec_InitDicAndState(TRUE, ...); */
    uint32_t checkDicSize;
    uint32_t state;
    uint32_t reps[4];
    uint32_t remainLen;
    uint32_t tempBufSize;
    uint32_t probs[probs_size];
    int needFlush;
    int needInitLzma;
    int needInitDic;
    int needInitState;
    int needInitProp;
    uint8_t tempBuf[LZMA_REQUIRED_INPUT_MAX];
    /* Contains the decompresison output, and used as the lookback dictionary.
     * allocCapacity bytes are allocated, it's OK to grow it up to dicfLimit.
     */
    uint8_t *dicf;
    uint8_t* readBuf;
    uint8_t* readCur;
    uint8_t* readEnd;
};

/* globals needed */
struct CLzmaDec* global;
int FUZZING;

/* Writes uncompressed data (global.dicf[global.writtenPos : global.dicfPos] to stdout. */
void Flush()
{
    /* print the bytes in the buffer until done */
    uint8_t* p = global->dicf + global->writtenPos;
    uint8_t* q = global->dicf + global->dicfPos;

    while(p < q)
    {
        fputc(0xFF & p[0], destination);
        p = p + 1;
    }

    global->writtenPos = global->dicfPos;
}

void FlushDiscardOldFromStartOfDic()
{
    if(global->dicfPos > global->dicSize)
    {
        uint32_t delta = global->dicfPos - global->dicSize;

        if(delta + MAX_MATCH_SIZE >= sizeof_writeBuf)
        {
            Flush();
            global->dicf = memmove(global->dicf, global->dicf + delta, global->dicSize);
            global->dicfPos = global->dicfPos - delta;
            global->dicfLimit = global->dicfLimit - delta;
            global->writtenPos = global->writtenPos - delta;
            global->discardedSize = global->discardedSize + delta;
        }
    }

}

void GrowCapacity(uint32_t newCapacity)
{
    if(newCapacity > global->allocCapacity)
    {
        /* make sure we don't alloc too much */
        require(newCapacity <= MAX_DICF_SIZE, "GrowCapacity exceeds MAX_DICF_SIZE");

        /* Get our new block */
        uint8_t* dicf = calloc(newCapacity, sizeof(uint8_t));
        require(NULL != dicf, "GrowCapacity memory allocation failed");

        /* copy our old block into it  and get rid of the old block */
        if (NULL != global->dicf) {
            memcpy(dicf, global->dicf, global->allocCapacity);
            free(global->dicf);
        }

        /* now track that new state */
        global->dicf = dicf;
        global->allocCapacity = newCapacity;
    }

    /* else no need to grow */
}

void FlushDiscardGrowDic(uint32_t dicfPosDelta)
{
    uint32_t minCapacity = global->dicfPos + dicfPosDelta;
    uint32_t newCapacity;

    if(minCapacity > global->allocCapacity)
    {
        FlushDiscardOldFromStartOfDic();
        minCapacity = global->dicfPos + dicfPosDelta;

        if(minCapacity > global->allocCapacity)
        {
            /* start by assuming 64KB */
            newCapacity = (1 << 16);

            while(newCapacity + MAX_MATCH_SIZE < minCapacity)
            {
                /* No overflow. */
                if(newCapacity > global->dicSize)
                {
                    newCapacity = global->dicSize;
                    if(newCapacity + MAX_MATCH_SIZE < minCapacity)
                    {
                        newCapacity = minCapacity - MAX_MATCH_SIZE;
                    }
                    break;
                }
                newCapacity = newCapacity << 1;
            }

            GrowCapacity(newCapacity + MAX_MATCH_SIZE);
        }
    }
}


void LzmaDec_DecodeReal(uint32_t limit, uint8_t *bufLimit)
{
    uint32_t *probs = global->probs;
    uint32_t state = global->state;
    uint32_t rep0 = global->reps[0];
    uint32_t rep1 = global->reps[1];
    uint32_t rep2 = global->reps[2];
    uint32_t rep3 = global->reps[3];
    uint32_t pbMask = (1 << (global->pb)) - 1;
    uint32_t lpMask = (1 << (global->lp)) - 1;
    uint32_t lc = global->lc;
    uint8_t* dicl = global->dicf;
    uint32_t diclLimit = global->dicfLimit;
    uint32_t diclPos = global->dicfPos;
    uint32_t processedPos = global->processedPos;
    uint32_t checkDicSize = global->checkDicSize;
    uint32_t len = 0;
    uint8_t* buf = global->buf;
    uint32_t range = global->range;
    uint32_t code = global->code;

    uint32_t* prob;
    uint32_t bound;
    uint32_t ttt;
    uint32_t posState;
    uint32_t symbol;
    uint32_t matchByte;
    uint32_t offs;
    uint32_t bit;
    uint32_t* probLit;
    uint32_t distance;
    uint32_t limita;
    uint32_t *probLen;
    uint32_t offset;
    uint32_t posSlot;
    uint32_t numDirectBits;
    uint32_t mask;
    uint32_t i;
    uint32_t n;
    uint32_t t;
    uint32_t rem;
    uint32_t curLen;
    uint32_t pos;
    uint8_t* p;

    do
    {
        posState = processedPos & pbMask;
        p = probs;
        prob = p + 4 * (IsMatch + (state << kNumPosBitsMax) + posState);
        ttt = prob[0];

        if(range < kTopValue)
        {
            range = range << 8;
            code = (code << 8) | (0xFF & buf[0]);
            buf = buf + 1;
        }

        bound = (range >> kNumBitModelTotalBits) * ttt;

        if(code < bound)
        {
            range = bound;
            prob[0] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & prob[0]);
            p = probs;
            prob = p + 4 * Literal;

            if(checkDicSize != 0 || processedPos != 0)
            {
                if(diclPos == 0)
                {
                    p = prob;
                    prob = p + 4 * (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + (0xFF & dicl[(diclLimit) - 1])) >> (8 - lc));
                }
                else
                {
                    p = prob;
                    prob = p + 4 *(LZMA_LIT_SIZE * ((((processedPos & lpMask) << lc) + (0xFF & dicl[diclPos - 1])) >> (8 - lc)));
                }
            }

            if(state < kNumLitStates)
            {
                if(state < 4) state = 0;
                else state = state - 3;
                symbol = 1;

                do
                {
                    ttt = prob[symbol];

                    if(range < kTopValue)
                    {
                        range = range << 8;
                        code = (code << 8) | (0xFF & buf[0]);
                        buf = buf + 1;
                    }

                    bound = (range >> kNumBitModelTotalBits) * ttt;

                    if(code < bound)
                    {
                        range = bound;
                        prob[symbol] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & prob[symbol]);
                        symbol = (symbol + symbol);
                    }
                    else
                    {
                        range = range - bound;
                        code = code - bound;
                        prob[symbol] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & prob[symbol]);
                        symbol = (symbol + symbol) + 1;
                    }
                } while(symbol < 0x100);
            }
            else
            {
                if(diclPos < rep0) matchByte = 0xFF & dicl[(diclPos - rep0) + diclLimit];
                else matchByte = 0xFF & dicl[(diclPos - rep0)];

                offs = 0x100;

                if(state < 10) state = state - 3;
                else state = state - 6;

                symbol = 1;

                do
                {
                    matchByte = matchByte << 1;
                    bit = (matchByte & offs);
                    p = prob;
                    probLit = p + 4 * (offs + bit + symbol);
                    ttt = probLit[0];

                    if(range < kTopValue)
                    {
                        range = range << 8;
                        code = (code << 8) | (0xFF & buf[0]);
                        buf = buf + 1;
                    }

                    bound = (range >> kNumBitModelTotalBits) * ttt;

                    if(code < bound)
                    {
                        range = bound;
                        probLit[0] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & probLit[0]);
                        symbol = (symbol + symbol);
                        offs = offs & ~bit;
                    }
                    else
                    {
                        range = range - bound;
                        code = code - bound;
                        probLit[0] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & probLit[0]);
                        symbol = (symbol + symbol) + 1;
                        offs = offs & bit;
                    }
                } while(symbol < 0x100);
            }

            if(diclPos >= global->allocCapacity)
            {
                global->dicfPos = diclPos;
                FlushDiscardGrowDic(1);
                dicl = global->dicf;
                diclLimit = global->dicfLimit;
                diclPos = global->dicfPos;
            }

            dicl[diclPos] = (0xFF & symbol) | ((~0xFF) & dicl[diclPos]);
            diclPos = diclPos + 1;
            processedPos = processedPos + 1;
            continue;
        }
        else
        {
            range = range - bound;
            code = code - bound;
            prob[0] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & prob[0]);
            p = probs;
            prob = p + 4 * (IsRep + state);
            ttt = prob[0];

            if(range < kTopValue)
            {
                range = range << 8;
                code = (code << 8) | (0xFF & buf[0]);
                buf = buf + 1;
            }

            bound = (range >> kNumBitModelTotalBits) * ttt;

            if(code < bound)
            {
                range = bound;
                prob[0] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & prob[0]);
                state = state + kNumStates;
                p = probs;
                prob = p + 4 * LenCoder;
            }
            else
            {
                range = range - bound;
                code = code - bound;
                prob[0] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & prob[0]);

                require((checkDicSize != 0) || (processedPos != 0), "checkDicsize == 0 && processPos == 0");

                p = probs;
                prob = p + 4 * (IsRepG0 + state);
                ttt = prob[0];

                if(range < kTopValue)
                {
                    range = range << 8;
                    code = (code << 8) | (0xFF & buf[0]);
                    buf = buf + 1;
                }

                bound = (range >> kNumBitModelTotalBits) * ttt;

                if(code < bound)
                {
                    range = bound;
                    prob[0] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & prob[0]);
                    p = probs;
                    prob = p + 4 * (IsRep0Long + (state << kNumPosBitsMax) + posState);
                    ttt = prob[0];

                    if(range < kTopValue)
                    {
                        range = range << 8;
                        code = (code << 8) | (0xFF & buf[0]);
                        buf = buf + 1;
                    }

                    bound = (range >> kNumBitModelTotalBits) * ttt;

                    if(code < bound)
                    {
                        range = bound;
                        prob[0] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & prob[0]);

                        if(diclPos >= global->allocCapacity)
                        {
                            global->dicfPos = diclPos;
                            FlushDiscardGrowDic(1);
                            dicl = global->dicf;
                            diclLimit = global->dicfLimit;
                            diclPos = global->dicfPos;
                        }

                        if(diclPos < rep0) dicl[diclPos] = (0xFF & dicl[(diclPos - rep0) + diclLimit]) | ((~0xFF) & dicl[diclPos]);
                        else dicl[diclPos] = (0xFF & dicl[(diclPos - rep0)]) | ((~0xFF) & dicl[diclPos]);

                        diclPos = diclPos + 1;
                        processedPos = processedPos + 1;

                        if(state < kNumLitStates) state = 9;
                        else state = 11;

                        continue;
                    }

                    range = range - bound;
                    code = code - bound;
                    prob[0] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & prob[0]);
                }
                else
                {
                    range = range - bound;
                    code = code - bound;
                    prob[0] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & prob[0]);
                    p = probs;
                    prob = p + 4 * (IsRepG1 + state);
                    ttt = prob[0];

                    if(range < kTopValue)
                    {
                        range = range << 8;
                        code = (code << 8) | (0xFF & buf[0]);
                        buf = buf + 1;
                    }

                    bound = (range >> kNumBitModelTotalBits) * ttt;

                    if(code < bound)
                    {
                        range = bound;
                        prob[0] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & prob[0]);
                        distance = rep1;
                    }
                    else
                    {
                        range = range - bound;
                        code = code - bound;
                        prob[0] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & prob[0]);
                        p = probs;
                        prob = p + 4 * (IsRepG2 + state);
                        ttt = prob[0];

                        if(range < kTopValue)
                        {
                            range = range << 8;
                            code = (code << 8) | (0xFF & buf[0]);
                            buf = buf + 1;
                        }

                        bound = (range >> kNumBitModelTotalBits) * ttt;

                        if(code < bound)
                        {
                            range = bound;
                            prob[0] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & prob[0]);
                            distance = rep2;
                        }
                        else
                        {
                            range = range - bound;
                            code = code - bound;
                            prob[0] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & prob[0]);
                            distance = rep3;
                            rep3 = rep2;
                        }

                        rep2 = rep1;
                    }

                    rep1 = rep0;
                    rep0 = distance;
                }

                if(state < kNumLitStates) state = 8;
                else state = 11;

                p = probs;
                prob = p + 4 * RepLenCoder;
            }

            p = prob;
            probLen = p + 4 * LenChoice;
            ttt = probLen[0];

            if(range < kTopValue)
            {
                range <<= 8;
                code = (code << 8) | (0xFF & buf[0]);
                buf = buf + 1;
            }

            bound = (range >> kNumBitModelTotalBits) * ttt;

            if(code < bound)
            {
                range = bound;
                probLen[0] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & probLen[0]);
                p = prob;
                probLen = p + 4 * (LenLow + (posState << kLenNumLowBits));
                offset = 0;
                limita = (1 << kLenNumLowBits);
            }
            else
            {
                range = range - bound;
                code = code - bound;
                probLen[0] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & probLen[0]);
                p = prob;
                probLen = p + 4 * LenChoice2;
                ttt = probLen[0];

                if(range < kTopValue)
                {
                    range = range << 8;
                    code = (code << 8) | (0xFF & buf[0]);
                    buf = buf + 1;
                }

                bound = (range >> kNumBitModelTotalBits) * ttt;

                if(code < bound)
                {
                    range = bound;
                    probLen[0] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & probLen[0]);
                    p = prob;
                    probLen = p + 4 * (LenMid + (posState << kLenNumMidBits));
                    offset = kLenNumLowSymbols;
                    limita = (1 << kLenNumMidBits);
                }
                else
                {
                    range = range - bound;
                    code = code - bound;
                    probLen[0] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & probLen[0]);
                    p = prob;
                    probLen = p + 4 * LenHigh;
                    offset = kLenNumLowSymbols + kLenNumMidSymbols;
                    limita = (1 << kLenNumHighBits);
                }
            }

            len = 1;

            do
            {
                ttt = probLen[len];

                if(range < kTopValue)
                {
                    range = range << 8;
                    code = (code << 8) | (0xFF & buf[0]);
                    buf = buf + 1;
                }

                bound = (range >> kNumBitModelTotalBits) * ttt;

                if(code < bound)
                {
                    range = bound;
                    probLen[len] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & probLen[len]);
                    len = (len + len);
                }
                else
                {
                    range = range - bound;
                    code = code - bound;
                    probLen[len] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & probLen[len]);
                    len = (len + len) + 1;
                }
            } while(len < limita);

            len = len - limita + offset;

            if(state >= kNumStates)
            {
                if(len < kNumLenToPosStates) { p = probs; prob = p + 4 * (PosSlot + (len << kNumPosSlotBits));  }
                else { p = probs; prob = p + 4 * (PosSlot + ((kNumLenToPosStates - 1) << kNumPosSlotBits));  }

                distance = 1;

                do
                {
                    ttt = prob[distance];

                    if(range < kTopValue)
                    {
                        range = range << 8;
                        code = (code << 8) | (0xFF & buf[0]);
                        buf = buf + 1;
                    }

                    bound = (range >> kNumBitModelTotalBits) * ttt;
                    if(code < bound)
                    {
                        range = bound;
                        prob[distance] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & prob[distance]);
                        distance = (distance + distance);
                    }
                    else
                    {
                        range = range - bound;
                        code = code - bound;
                        prob[distance] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & prob[distance]);
                        distance = (distance + distance) + 1;
                    }
                } while(distance < (1 << 6));

                distance = distance - (1 << 6);

                if(distance >= kStartPosModelIndex)
                {
                    posSlot = distance;
                    numDirectBits = (distance >> 1) - 1;
                    distance = (2 | (distance & 1));

                    if(posSlot < kEndPosModelIndex)
                    {
                        distance = distance << numDirectBits;
                        p = probs;
                        prob = p + 4 * (SpecPos + distance - posSlot - 1);
                        mask = 1;
                        i = 1;

                        do
                        {
                            ttt = prob[i];

                            if(range < kTopValue)
                            {
                                range = range << 8;
                                code = (code << 8) | (0xFF & buf[0]);
                                buf = buf + 1;
                            }

                            bound = (range >> kNumBitModelTotalBits) * ttt;

                            if(code < bound)
                            {
                                range = bound;
                                prob[i] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & prob[i]);
                                i = (i + i);
                            }
                            else
                            {
                                range = range - bound;
                                code = code - bound;
                                prob[i] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & prob[i]);
                                i = (i + i) + 1;
                                distance = distance | mask;
                            }

                            mask = mask << 1;
                            numDirectBits = numDirectBits - 1;
                        } while(numDirectBits != 0);
                    }
                    else
                    {
                        numDirectBits = numDirectBits - kNumAlignBits;

                        do
                        {
                            if(range < kTopValue)
                            {
                                range = range << 8;
                                code = (code << 8) | (0xFF & buf[0]);
                                buf = buf + 1;
                            }

                            range = range >> 1;
                            {
                                code = code - range;
                                t = (0 - (code >> 31));
                                distance = (distance << 1) + (t + 1);
                                code = code + (range & t);
                            }
                            numDirectBits = numDirectBits - 1;
                        } while(numDirectBits != 0);

                        p = probs;
                        prob = p + 4 * Align;
                        distance = distance << kNumAlignBits;
                        i = 1;
                        ttt = prob[i];

                        if(range < kTopValue)
                        {
                            range = range << 8;
                            code = (code << 8) | (0xFF & buf[0]);
                            buf = buf + 1;
                        }

                        bound = (range >> kNumBitModelTotalBits) * ttt;

                        if(code < bound)
                        {
                            range = bound;
                            prob[i] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & prob[i]);
                            i = (i + i);
                        }
                        else
                        {
                            range = range - bound;
                            code = code - bound;
                            prob[i] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & prob[i]);
                            i = (i + i) + 1;
                            distance = distance | 1;
                        }

                        ttt = prob[i];

                        if(range < kTopValue)
                        {
                            range = range << 8;
                            code = (code << 8) | (0xFF & buf[0]);
                            buf = buf + 1;
                        }

                        bound = (range >> kNumBitModelTotalBits) * ttt;

                        if(code < bound)
                        {
                            range = bound;
                            prob[i] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & prob[i]);
                            i = (i + i);
                        }
                        else
                        {
                            range = range - bound;
                            code = code - bound;
                            prob[i] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & prob[i]);
                            i = (i + i) + 1;
                            distance = distance | 2;
                        }

                        ttt = prob[i];

                        if(range < kTopValue)
                        {
                            range = range << 8;
                            code = (code << 8) | (0xFF & buf[0]);
                            buf = buf + 1;
                        }

                        bound = (range >> kNumBitModelTotalBits) * ttt;

                        if(code < bound)
                        {
                            range = bound;
                            prob[i] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & prob[i]);
                            i = (i + i);
                        }
                        else
                        {
                            range = range - bound;
                            code = code - bound;
                            prob[i] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & prob[i]);
                            i = (i + i) + 1;
                            distance = distance | 4;
                        }

                        ttt = prob[i];

                        if(range < kTopValue)
                        {
                            range = range << 8;
                            code = (code << 8) | (0xFF & buf[0]);
                            buf = buf + 1;
                        }

                        bound = (range >> kNumBitModelTotalBits) * ttt;

                        if(code < bound)
                        {
                            range = bound;
                            prob[i] = (BITS32 & ((ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)))) | (HIGHBITS & prob[i]);
                            i = (i + i);
                        }
                        else
                        {
                            range = range - bound;
                            code = code - bound;
                            prob[i] = (BITS32 & ((ttt - (ttt >> kNumMoveBits)))) | (HIGHBITS & prob[i]);
                            i = (i + i) + 1;
                            distance = distance | 8;
                        }

                        if(distance == BITS32)
                        {
                            len = len + kMatchSpecLenStart;
                            state = state - kNumStates;
                            break;
                        }
                    }
                }

                rep3 = rep2;
                rep2 = rep1;
                rep1 = rep0;
                rep0 = distance + 1;

                if(checkDicSize == 0) require(distance < processedPos , "distance >= processedPos");
                else require(distance < checkDicSize, "distance >= checkDicSize");

                if(state < kNumStates + kNumLitStates) state = kNumLitStates;
                else state = kNumLitStates + 3;
            }

            len = len + kMatchMinLen;
            require(len <= MAX_MATCH_SIZE, "len greater than MAX_MATCH_SIZE");
            require(limit != diclPos, "limit == diclPos");

            rem = limit - diclPos;
            if(rem < len) curLen = rem;
            else curLen = len;

            if(diclPos < rep0) pos = (diclPos - rep0) + diclLimit;
            else pos = diclPos - rep0;

            processedPos = processedPos + curLen;
            len = len - curLen;

            /* TODO(pts): ASSERT(len == curLen);, simplify buffering code. */
            /* + cannot overflow. */
            if((diclPos + curLen) > global->allocCapacity)
            {
                global->dicfPos = diclPos;
                FlushDiscardGrowDic(curLen);

                pos = pos + global->dicfPos - diclPos;
                dicl = global->dicf;
                diclLimit = global->dicfLimit;
                diclPos = global->dicfPos;
            }

            if((pos + curLen) <= diclLimit)
            {
                require(diclPos > pos, "diclPos > pos");
                require(curLen > 0, "curLen > 0");
                i = 0;
                n = curLen;
                /* overlapping memcpy of sorts */
                while(n > 0)
                {
                    dicl[diclPos + i] = (0xFF & dicl[pos + i]) | ((~0xFF) & dicl[diclPos + i]);
                    i = i + 1;
                    n = n - 1;
                }
                diclPos = diclPos + curLen;
            }
            else
            {
                do
                {
                    dicl[diclPos] = (0xFF & dicl[pos]) | ((~0xFF) & dicl[diclPos]);
                    diclPos = diclPos + 1;
                    pos = pos + 1;

                    if(pos == diclLimit)
                    {
                        pos = 0;
                    }
                    curLen = curLen - 1;
                } while(curLen != 0);
            }
        }
    } while((diclPos < limit) && (buf < bufLimit));

    if(range < kTopValue)
    {
        range = range << 8;
        code = (code << 8) | (0xFF & buf[0]);
        buf = buf + 1;
    }

    global->buf = buf;
    global->range = range;
    global->code = code;
    global->remainLen = len;
    global->dicfPos = diclPos;
    global->processedPos = processedPos;
    global->reps[0] = rep0;
    global->reps[1] = rep1;
    global->reps[2] = rep2;
    global->reps[3] = rep3;
    global->state = state;
}

void LzmaDec_WriteRem(uint32_t limit)
{
    uint8_t *dicl;
    uint32_t diclPos;
    uint32_t diclLimit;
    uint32_t len;
    uint32_t rep0;

    if(global->remainLen != 0 && global->remainLen < kMatchSpecLenStart)
    {
        dicl = global->dicf;
        diclPos = global->dicfPos;
        diclLimit = global->dicfLimit;
        len = global->remainLen;
        rep0 = global->reps[0];

        if(limit - diclPos < len)
        {
            len = limit - diclPos;
        }

        if(diclPos + len > global->allocCapacity)
        {
            FlushDiscardGrowDic(len);
            dicl = global->dicf;
            diclLimit = global->dicfLimit;
            diclPos = global->dicfPos;
        }

        if((global->checkDicSize == 0) && ((global->dicSize - global->processedPos) <= len))
        {
            global->checkDicSize = global->dicSize;
        }

        global->processedPos = global->processedPos + len;
        global->remainLen = global->remainLen - len;

        while(len != 0)
        {
            len = len - 1;
            if(diclPos < rep0) dicl[diclPos] = (0xFF & dicl[(diclPos - rep0) + diclLimit]) | ((~0xFF) & dicl[diclPos]);
            else dicl[diclPos] = (0xFF & dicl[diclPos - rep0]) | ((~0xFF) & dicl[diclPos]);
            diclPos = diclPos + 1;
        }

        global->dicfPos = diclPos;
    }
}

void LzmaDec_DecodeReal2(uint32_t limit, uint8_t *bufLimit)
{
    uint32_t limit2;
    uint32_t rem;

    do
    {
        limit2 = limit;

        if(global->checkDicSize == 0)
        {
            rem = global->dicSize - global->processedPos;

            if((limit - global->dicfPos) > rem)
            {
                limit2 = global->dicfPos + rem;
            }
        }

        LzmaDec_DecodeReal(limit2, bufLimit);

        if(global->processedPos >= global->dicSize)
        {
            global->checkDicSize = global->dicSize;
        }

        LzmaDec_WriteRem(limit);
    } while((global->dicfPos < limit) && (global->buf < bufLimit) && (global->remainLen < kMatchSpecLenStart));

    if(global->remainLen > kMatchSpecLenStart)
    {
        global->remainLen = kMatchSpecLenStart;
    }
}

int LzmaDec_TryDummy(uint8_t* buf, uint32_t inSize)
{
    uint32_t range = global->range;
    uint32_t code = global->code;
    uint8_t* bufLimit = buf + inSize;
    uint32_t* probs = global->probs;
    uint32_t state = global->state;
    int res;
    uint32_t* prob;
    uint32_t bound;
    uint32_t ttt;
    uint32_t posState;
    uint32_t hold;
    uint32_t symbol;
    uint32_t matchByte;
    uint32_t offs;
    uint32_t bit;
    uint32_t* probLit;
    uint32_t len;
    uint32_t limit;
    uint32_t offset;
    uint32_t* probLen;
    uint32_t posSlot;
    uint32_t numDirectBits;
    uint32_t i;
    uint8_t* p;

    posState = (global->processedPos) & ((1 << global->pb) - 1);
    p = probs;
    prob = p + 4 * (IsMatch + (state << kNumPosBitsMax) + posState);
    ttt = prob[0];

    if(range < kTopValue)
    {
        if(buf >= bufLimit)
        {
            return DUMMY_ERROR;
        }

        range = range << 8;
        code = (code << 8) | (0xFF & buf[0]);
        buf = buf + 1;
    }

    bound = (range >> kNumBitModelTotalBits) * ttt;

    if(code < bound)
    {
        range = bound;
        p = probs;
        prob = p + 4 * Literal;

        if(global->checkDicSize != 0 || global->processedPos != 0)
        {
            hold = (((global->processedPos) & ((1 << (global->lp)) - 1)) << global->lc);
            if(global->dicfPos == 0)
            {
                hold = hold + ((0xFF & global->dicf[global->dicfLimit - 1]) >> (8 - global->lc));
            }
            else
            {
                hold = hold + ((0xFF & global->dicf[global->dicfPos - 1]) >> (8 - global->lc));
            }
            p = prob;
            prob = p + 4 * (LZMA_LIT_SIZE * hold);
        }

        if(state < kNumLitStates)
        {
            symbol = 1;

            do
            {
                ttt = prob[symbol];

                if(range < kTopValue)
                {
                    if(buf >= bufLimit)
                    {
                        return DUMMY_ERROR;
                    }

                    range = range << 8;
                    code = (code << 8) | (0xFF & buf[0]);
                    buf = buf + 1;
                }

                bound = (range >> kNumBitModelTotalBits) * ttt;

                if(code < bound)
                {
                    range = bound;
                    symbol = (symbol + symbol);
                }
                else
                {
                    range = range - bound;
                    code = code - bound;
                    symbol = (symbol + symbol) + 1;
                }
            } while(symbol < 0x100);
        }
        else
        {
            if(global->dicfPos < (global->reps[0] & BITS32))
            {
                hold = global->dicfPos - (global->reps[0] & BITS32) + global->dicfLimit;
            }
            else hold = global->dicfPos - (global->reps[0] & BITS32);
            matchByte = 0xFF & global->dicf[hold];

            offs = 0x100;
            symbol = 1;

            do
            {
                matchByte = matchByte << 1;
                bit = (matchByte & offs);
                p = prob;
                probLit = p + 4 * (offs + bit + symbol);
                ttt = probLit[0];

                if(range < kTopValue)
                {
                    if(buf >= bufLimit)
                    {
                        return DUMMY_ERROR;
                    }

                    range = range << 8;
                    code = (code << 8) | (0xFF & buf[0]);
                    buf = buf + 1;
                }

                bound = (range >> kNumBitModelTotalBits) * ttt;

                if(code < bound)
                {
                    range = bound;
                    symbol = (symbol + symbol);
                    offs = offs & ~bit;
                }
                else
                {
                    range = range - bound;
                    code = code - bound;
                    symbol = (symbol + symbol) + 1;
                    offs = offs & bit;
                }
            } while(symbol < 0x100);
        }

        res = DUMMY_LIT;
    }
    else
    {
        range = range - bound;
        code = code - bound;
        p = probs;
        prob = p + 4 * (IsRep + state);
        ttt = prob[0];

        if(range < kTopValue)
        {
            if(buf >= bufLimit)
            {
                return DUMMY_ERROR;
            }

            range = range << 8;
            code = (code << 8) | (0xFF & buf[0]);
            buf = buf + 1;
        }

        bound = (range >> kNumBitModelTotalBits) * ttt;

        if(code < bound)
        {
            range = bound;
            state = 0;
            p = probs;
            prob = p + 4 * LenCoder;
            res = DUMMY_MATCH;
        }
        else
        {
            range = range - bound;
            code = code - bound;
            res = DUMMY_REP;
            p = probs;
            prob = p + 4 * (IsRepG0 + state);
            ttt = prob[0];

            if(range < kTopValue)
            {
                if(buf >= bufLimit)
                {
                    return DUMMY_ERROR;
                }

                range = range << 8;
                code = (code << 8) | (0xFF & buf[0]);
                buf = buf + 1;
            }

            bound = (range >> kNumBitModelTotalBits) * ttt;

            if(code < bound)
            {
                range = bound;
                p = probs;
                prob = p + 4 * (IsRep0Long + (state << kNumPosBitsMax) + posState);
                ttt = prob[0];

                if(range < kTopValue)
                {
                    if(buf >= bufLimit)
                    {
                        return DUMMY_ERROR;
                    }

                    range = range << 8;
                    code = (code << 8) | (0xFF & buf[0]);
                    buf = buf + 1;
                }

                bound = (range >> kNumBitModelTotalBits) * ttt;

                if(code < bound)
                {
                    range = bound;

                    if(range < kTopValue)
                    {
                        if(buf >= bufLimit)
                        {
                            return DUMMY_ERROR;
                        }

                        range = range << 8;
                        code = (code << 8) | (0xFF & buf[0]);
                        buf = buf + 1;
                    }

                    return DUMMY_REP;
                }
                else
                {
                    range = range - bound;
                    code = code - bound;
                }
            }
            else
            {
                range = range - bound;
                code = code - bound;
                p = probs;
                prob = p + 4 * (IsRepG1 + state);
                ttt = prob[0];

                if(range < kTopValue)
                {
                    if(buf >= bufLimit)
                    {
                        return DUMMY_ERROR;
                    }

                    range = range << 8;
                    code = (code << 8) | (0xFF & buf[0]);
                    buf = buf + 1;
                }

                bound = (range >> kNumBitModelTotalBits) * ttt;

                if(code < bound)
                {
                    range = bound;
                }
                else
                {
                    range = range - bound;
                    code = code - bound;
                    p = probs;
                    prob = p + 4 * (IsRepG2 + state);
                    ttt = prob[0];

                    if(range < kTopValue)
                    {
                        if(buf >= bufLimit)
                        {
                            return DUMMY_ERROR;
                        }

                        range = range << 8;
                        code = (code << 8) | (0xFF & buf[0]);
                        buf = buf + 1;
                    }

                    bound = (range >> kNumBitModelTotalBits) * ttt;

                    if(code < bound)
                    {
                        range = bound;
                    }
                    else
                    {
                        range = range - bound;
                        code = code - bound;
                    }
                }
            }

            state = kNumStates;
            p = probs;
            prob = p + 4 * RepLenCoder;
        }

        p = prob;
        probLen = p + 4 * LenChoice;
        ttt = probLen[0];

        if(range < kTopValue)
        {
            if(buf >= bufLimit)
            {
                return DUMMY_ERROR;
            }

            range = range << 8;
            code = (code << 8) | (0xFF & buf[0]);
            buf = buf + 1;
        }

        bound = (range >> kNumBitModelTotalBits) * ttt;

        if(code < bound)
        {
            range = bound;
            p = prob;
            probLen = p + 4 * (LenLow + (posState << kLenNumLowBits));
            offset = 0;
            limit = 1 << kLenNumLowBits;
        }
        else
        {
            range = range - bound;
            code = code - bound;
            p = prob;
            probLen = p + 4 * LenChoice2;
            ttt = probLen[0];

            if(range < kTopValue)
            {
                if(buf >= bufLimit)
                {
                    return DUMMY_ERROR;
                }

                range = range << 8;
                code = (code << 8) | (0xFF & buf[0]);
                buf = buf + 1;
            }

            bound = (range >> kNumBitModelTotalBits) * ttt;

            if(code < bound)
            {
                range = bound;
                p = prob;
                probLen = p + 4 * (LenMid + (posState << kLenNumMidBits));
                offset = kLenNumLowSymbols;
                limit = 1 << kLenNumMidBits;
            }
            else
            {
                range = range - bound;
                code = code - bound;
                probLen = p + 4 * LenHigh;
                offset = kLenNumLowSymbols + kLenNumMidSymbols;
                limit = 1 << kLenNumHighBits;
            }
        }

        len = 1;

        do
        {
            ttt = probLen[len];

            if(range < kTopValue)
            {
                if(buf >= bufLimit)
                {
                    return DUMMY_ERROR;
                }

                range = range << 8;
                code = (code << 8) | (0xFF & buf[0]);
                buf = buf + 1;
            }

            bound = (range >> kNumBitModelTotalBits) * ttt;

            if(code < bound)
            {
                range = bound;
                len = (len + len);
            }
            else
            {
                range = range - bound;
                code = code - bound;
                len = (len + len) + 1;
            }
        } while(len < limit);

        len = len - limit + offset;

        if(state < 4)
        {
            if(len < kNumLenToPosStates) hold = len << kNumPosSlotBits;
            else hold = (kNumLenToPosStates - 1) << kNumPosSlotBits;

            p = probs;
            prob = p + 4 * (PosSlot + hold);
            posSlot = 1;

            do
            {
                ttt = prob[posSlot];

                if(range < kTopValue)
                {
                    if(buf >= bufLimit)
                    {
                        return DUMMY_ERROR;
                    }

                    range = range << 8;
                    code = (code << 8) | (0xFF & buf[0]);
                    buf = buf + 1;
                }

                bound = (range >> kNumBitModelTotalBits) * ttt;

                if(code < bound)
                {
                    range = bound;
                    posSlot = (posSlot + posSlot);
                }
                else
                {
                    range = range - bound;
                    code = code - bound;
                    posSlot = (posSlot + posSlot) + 1;
                }
            } while(posSlot < (1 << kNumPosSlotBits));

            posSlot = posSlot - (1 << kNumPosSlotBits);

            if(posSlot >= kStartPosModelIndex)
            {
                numDirectBits = ((posSlot >> 1) - 1);

                if(posSlot < kEndPosModelIndex)
                {
                    p = probs;
                    prob = p + 4 * (SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1);
                }
                else
                {
                    numDirectBits = numDirectBits - kNumAlignBits;

                    do
                    {
                        if(range < kTopValue)
                        {
                            if(buf >= bufLimit)
                            {
                                return DUMMY_ERROR;
                            }

                            range = range << 8;
                            code = (code << 8) | (0xFF & buf[0]);
                            buf = buf + 1;
                        }

                        range = range >> 1;
                        code = code - (range & ((((code - range) >> 31) & 1) - 1));
                        numDirectBits = numDirectBits - 1;
                    } while(numDirectBits != 0);

                    p = probs;
                    prob = p + 4 * Align;
                    numDirectBits = kNumAlignBits;
                }

                i = 1;

                do
                {
                    ttt = prob[i];

                    if(range < kTopValue)
                    {
                        if(buf >= bufLimit)
                        {
                            return DUMMY_ERROR;
                        }

                        range = range << 8;
                        code = (code << 8) | (0xFF & buf[0]);
                        buf = buf + 1;
                    }

                    bound = (range >> kNumBitModelTotalBits) * ttt;

                    if(code < bound)
                    {
                        range = bound;
                        i = (i + i);
                    }
                    else
                    {
                        range = range - bound;
                        code = code - bound;
                        i = (i + i) + 1;
                    }
                    numDirectBits = numDirectBits - 1;
                } while(numDirectBits != 0);
            }
        }
    }

    if(range < kTopValue)
    {
        if(buf >= bufLimit)
        {
            return DUMMY_ERROR;
        }

        /* is this even needed? */
        range = range << 8;
        code = (code << 8) | (0xFF & buf[0]);
        buf = buf + 1;
    }

    return res;
}


void LzmaDec_InitRc(uint8_t* data)
{
    global->code = ((0xFF & data[1]) << 24) | ((0xFF & data[2]) << 16) | ((0xFF & data[3]) << 8) | (0xFF & data[4]);
    global->range = BITS32;
    global->needFlush = FALSE;
}

void LzmaDec_InitDicAndState(int initDic, int initState)
{
    global->needFlush = TRUE;
    global->remainLen = 0;
    global->tempBufSize = 0;

    if(initDic)
    {
        global->processedPos = 0;
        global->checkDicSize = 0;
        global->needInitLzma = TRUE;
    }

    if(initState)
    {
        global->needInitLzma = TRUE;
    }
}

void LzmaDec_InitStateReal()
{
    uint32_t numProbs = Literal + (LZMA_LIT_SIZE << (global->lc + global->lp));
    uint32_t i;
    uint32_t* probs = global->probs;

    for(i = 0; i < numProbs; i = i + 1)
    {
        probs[i] = (BITS32 & (kBitModelTotal >> 1)) | (HIGHBITS & probs[i]);
    }

    global->reps[0] = 1; global->reps[1] = 1; global->reps[2] = 1; global->reps[3] = 1;
    global->state = 0;
    global->needInitLzma = FALSE;
}

uint32_t LzmaDec_DecodeToDic(uint8_t* src, uint32_t srcLen)
{
    uint32_t srcLen0 = srcLen;
    uint32_t inSize = srcLen;
    int checkEndMarkNow;
    uint32_t processed;
    uint8_t *bufLimit;
    uint32_t dummyRes;
    uint32_t rem;
    uint32_t lookAhead;

    srcLen = 0;
    LzmaDec_WriteRem(global->dicfLimit);

    while(global->remainLen != kMatchSpecLenStart)
    {
        if(global->needFlush)
        {
            while(inSize > 0 && global->tempBufSize < RC_INIT_SIZE)
            {
                global->tempBuf[global->tempBufSize] = 0xFF & src[0];
                global->tempBufSize = global->tempBufSize + 1;
                src = src + 1;
                srcLen = srcLen + 1;
                inSize = inSize - 1;
            }

            if(global->tempBufSize < RC_INIT_SIZE)
            {
                if(srcLen != srcLen0) return SZ_ERROR_NEEDS_MORE_INPUT_PARTIAL;
                return SZ_ERROR_NEEDS_MORE_INPUT;
            }

            if((0xFF & global->tempBuf[0]) != 0) return SZ_ERROR_DATA;

            LzmaDec_InitRc(global->tempBuf);
            global->tempBufSize = 0;
        }

        checkEndMarkNow = FALSE;

        if(global->dicfPos >= global->dicfLimit)
        {
            if((global->remainLen == 0) && (global->code == 0))
            {
                if(srcLen != srcLen0) return SZ_ERROR_CHUNK_NOT_CONSUMED;
                return SZ_OK /* MAYBE_FINISHED_WITHOUT_MARK */;
            }

            if(global->remainLen != 0) return SZ_ERROR_NOT_FINISHED;
            checkEndMarkNow = TRUE;
        }

        if(global->needInitLzma) LzmaDec_InitStateReal();

        if(global->tempBufSize == 0)
        {

            if(inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
            {
                dummyRes = LzmaDec_TryDummy(src, inSize);

                if(dummyRes == DUMMY_ERROR)
                {
                    memcpy(global->tempBuf, src, inSize);
                    global->tempBufSize = inSize;
                    srcLen += inSize;
                    if(srcLen != srcLen0) return SZ_ERROR_NEEDS_MORE_INPUT_PARTIAL;
                    return SZ_ERROR_NEEDS_MORE_INPUT;
                }

                if(checkEndMarkNow && dummyRes != DUMMY_MATCH) return SZ_ERROR_NOT_FINISHED;
                bufLimit = src;
            }
            else
            {
                bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX;
            }

            global->buf = src;
            LzmaDec_DecodeReal2(global->dicfLimit, bufLimit);
            processed = (global->buf - src);
            srcLen = srcLen + processed;
            src = src + processed;
            inSize = inSize - processed;
        }
        else
        {
            rem = global->tempBufSize;
            lookAhead = 0;

            while((rem < LZMA_REQUIRED_INPUT_MAX) && (lookAhead < inSize))
            {
                global->tempBuf[rem] = 0xFF & src[lookAhead];
                rem = rem + 1;
                lookAhead = lookAhead + 1;
            }

            global->tempBufSize = rem;

            if(rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
            {
                dummyRes = LzmaDec_TryDummy(global->tempBuf, rem);

                if(dummyRes == DUMMY_ERROR)
                {
                    srcLen = srcLen + lookAhead;
                    if(srcLen != srcLen0) return SZ_ERROR_NEEDS_MORE_INPUT_PARTIAL;
                    return SZ_ERROR_NEEDS_MORE_INPUT;
                }

                if(checkEndMarkNow && dummyRes != DUMMY_MATCH) return SZ_ERROR_NOT_FINISHED;
            }

            global->buf = global->tempBuf;
            LzmaDec_DecodeReal2(global->dicfLimit, global->buf);
            lookAhead = lookAhead - (rem - (global->buf - global->tempBuf));
            srcLen = srcLen + lookAhead;
            src = src + lookAhead;
            inSize = inSize - lookAhead;
            global->tempBufSize = 0;
        }
    }

    if(global->code != 0) return SZ_ERROR_DATA;
    return SZ_ERROR_FINISHED_WITH_MARK;
}



/* Tries to preread r bytes to the read buffer. Returns the number of bytes
 * available in the read buffer. If smaller than r, that indicates EOF.
 *
 * Doesn't try to preread more than absolutely necessary, to avoid copies in
 * the future.
 *
 * Works only if r <= sizeof(readBuf).
 */
uint32_t Preread(uint32_t r)
{
    uint32_t hold;
    uint32_t p = global->readEnd - global->readCur;
    require(r <= sizeof_readBuf, "r <= sizeof_readBuf");

    if(p < r)     /* Not enough pending available. */
    {
        if(global->readBuf + sizeof_readBuf - global->readCur + 0 < r)
        {
            /* If no room for r bytes to the end, discard bytes from the beginning. */
            global->readBuf = memmove(global->readBuf, global->readCur, p);
            global->readEnd = global->readBuf + p;
            global->readCur = global->readBuf;
        }

        while(p < r)
        {
            /* our single spot for reading input */
            hold = fgetc(source);
            pos = pos + 1;
            /* EOF or error on input. */
            if(EOF == hold) break;

            /* otherwise just add it */
            global->readEnd[0] = (0xFF & hold) | ((~0xFF) & global->readEnd[0]);
            global->readEnd = global->readEnd + 1;
            p = p + 1;
        }
    }

    return p;
}

void IgnoreVarint()
{
    while((0xFF & global->readCur[0]) >= 0x80)
    {
        global->readCur = global->readCur + 1;
    }
    global->readCur = global->readCur + 1;
}

uint32_t IgnoreZeroBytes(uint32_t c)
{
    while(c > 0)
    {
        if((0xFF & global->readCur[0]) != 0)
        {
            global->readCur = global->readCur + 1;
            return SZ_ERROR_BAD_PADDING;
        }
        global->readCur = global->readCur + 1;
        c = c - 1;
    }

    return SZ_OK;
}

uint32_t GetLE4(uint8_t *p)
{
    return (0xFF & p[0]) | (0xFF & p[1]) << 8 | (0xFF & p[2]) << 16 | (0xFF & p[3]) << 24;
}

/* Expects global->dicSize be set already. Can be called before or after InitProp. */
void InitDecode()
{
    /* global->lc = global->pb = global->lp = 0; */  /* needinitprop will initialize it */
    global->dicfLimit = 0;  /* We'll increment it later. */
    global->needInitDic = TRUE;
    global->needInitState = TRUE;
    global->needInitProp = TRUE;
    global->writtenPos = 0;
    global->writeRemaining = BITS32;
    global->discardedSize = 0;
    global->dicfPos = 0;
    LzmaDec_InitDicAndState(TRUE, TRUE);
}

uint32_t InitProp(uint8_t b)
{
    uint32_t lc;
    uint32_t lp;

    if(b >= (9 * 5 * 5))
    {
        return SZ_ERROR_BAD_LCLPPB_PROP;
    }

    lc = b % 9;
    b = b / 9;
    global->pb = b / 5;
    lp = b % 5;

    if(lc + lp > LZMA2_LCLP_MAX)
    {
        return SZ_ERROR_BAD_LCLPPB_PROP;
    }

    global->lc = lc;
    global->lp = lp;
    global->needInitProp = FALSE;
    return SZ_OK;
}


/* Reads .xz or .lzma data from source, writes uncompressed bytes to destination,
 * uses CLzmaDec.dic. It verifies some aspects of the file format (so it
 * can't be tricked to an infinite loop etc.), it doesn't verify checksums
 * (e.g. CRC32).
 */
uint32_t DecompressXzOrLzma()
{
    uint8_t checksumSize;
    /* Block header flags */
    uint32_t bhf;
    uint32_t result;
    /* uncompressed chunk size*/
    uint32_t us;

    /* needed by lzma */
    uint32_t srcLen;
    uint32_t res;

    /* needed by xz */
    uint8_t blockSizePad;
    uint32_t bhs;
    uint32_t bhs2;
    uint8_t dicSizeProp;
    uint8_t* readAtBlock;
    uint8_t control;
    uint8_t numRecords;
    /* compressed chunk size */
    uint32_t cs;
    int initDic;
    uint8_t mode;
    int initState;
    int isProp;

    /* 12 for the stream header + 12 for the first block header + 6 for the
     * first chunk header. empty.xz is 32 bytes.
     */
    if(Preread(12 + 12 + 6) < 12 + 12 + 6)
    {
        return SZ_ERROR_INPUT_EOF;
    }

    /* readbuf[7] is actually stream flags, should also be 0. */
    if(0 != memcmp(global->readCur, "\xFD""7zXZ\0", 7))
    {
        /* sanity check for lzma */
        require((0xFF & global->readCur[0]) <= 225, "lzma check 1 failed");
        require((0xFF & global->readCur[13]) == 0, "lzma check 2 failed");
        require((((bhf = GetLE4(global->readCur + 9)) == 0) || (bhf == BITS32)), "lzma check 3 failed");
        require((global->dicSize = GetLE4(global->readCur + 1)) >= LZMA_DIC_MIN, "lzma check 4 failed");

        /* Based on https://svn.python.org/projects/external/xz-5.0.3/doc/lzma-file-format.txt */
        /* TODO(pts): Support 8-byte uncompressed size. */
        if(bhf == 0) us = GetLE4(global->readCur + 5);
        else us = bhf;

        if(global->dicSize > MAX_DIC_SIZE) return SZ_ERROR_UNSUPPORTED_DICTIONARY_SIZE;

        InitDecode();
        global->allocCapacity = 0;
        global->dicf = NULL;
        /* LZMA2 restricts lc + lp <= 4. LZMA requires lc + lp <= 12.
         * We apply the LZMA2 restriction here (to save memory in
         * CLzmaDec.probs), thus we are not able to extract some legitimate
         * .lzma files.
         */
        result = (InitProp(0xFF & global->readCur[0]));
        if(result != SZ_OK) return result;

        global->readCur = global->readCur + 13;  /* Start decompressing the 0 byte. */
        global->dicfLimit = global->writeRemaining;
        global->writeRemaining = us;

        if(us <= global->dicSize) GrowCapacity(us);

        while((global->discardedSize + global->dicfPos) != us)
        {

            if((srcLen = Preread(sizeof_readBuf)) == 0)
            {
                if(us != BITS32) return SZ_ERROR_INPUT_EOF;
                break;
            }

            res = LzmaDec_DecodeToDic(global->readCur, srcLen);
            global->readCur = global->readCur + srcLen;

            if(res == SZ_ERROR_FINISHED_WITH_MARK) break;

            if(res != SZ_ERROR_NEEDS_MORE_INPUT && res != SZ_OK) return res;
        }

        Flush();
        return SZ_OK;
    }

    global->allocCapacity = 0;
    global->dicf = NULL;

    while(TRUE)
    {
        /* Based on https://tukaani.org/xz/xz-file-format-1.0.4.txt */
        switch(0xFF & global->readCur[7])
        {
            /* None */
            case 0: checksumSize = 1;
                    break;
            /* CRC32 */
            case 1: checksumSize = 4;
                    break;
            /* CRC64, typical xz output. */
            case 4: checksumSize = 8;
                    break;
            default: return SZ_ERROR_BAD_CHECKSUM_TYPE;
        }

        /* Also ignore the CRC32 after checksumSize. */
        global->readCur = global->readCur + 12;

        while(TRUE)
        {
            /* We need it modulo 4, so a uint8_t is enough. */
            blockSizePad = 3;
            require(global->readEnd - global->readCur >= 12, "readEnd - readCur >= 12");  /* At least 12 bytes preread. */

            bhs = 0xFF & global->readCur[0];
            /* Last block, index follows. */
            if(bhs == 0)
            {
                global->readCur = global->readCur + 1;
                /* This is actually a varint, but it's shorter to read it as a byte. */
                numRecords = 0xFF & global->readCur[0];
                global->readCur = global->readCur + 1;
                while(0 != numRecords) {
                    /* a varint is at most 9 bytes long, but may be shorter */
                    Preread(9);
                    IgnoreVarint();
                    Preread(9);
                    IgnoreVarint();
                    numRecords = numRecords - 1;
                }
                /* Synchronize to 4-byte boundary */
                if (0 != ((pos - (global->readEnd - global->readCur)) & 3)) {
                    Preread(4 - ((pos - (global->readEnd - global->readCur)) & 3));
                    global->readCur = global->readCur + (4 - ((pos - (global->readEnd - global->readCur)) & 3));
                }
                /* Consume crc32 of index + stream footer */
                Preread(16);
                global->readCur = global->readCur + 16;
                break;
            }
            global->readCur = global->readCur + 1;

            /* Block header size includes the bhs field above and the CRC32 below. */
            bhs = (bhs + 1) << 2;

            /* Typically the Preread(12 + 12 + 6) above covers it. */
            if(Preread(bhs) < bhs)
            {
                return SZ_ERROR_INPUT_EOF;
            }

            readAtBlock = global->readCur;
            bhf = 0xFF & global->readCur[0];
            global->readCur = global->readCur + 1;

            if((bhf & 2) != 0) return SZ_ERROR_UNSUPPORTED_FILTER_COUNT;
            if((bhf & 20) != 0) return SZ_ERROR_BAD_BLOCK_FLAGS;
            /* Compressed size present. */
            /* Usually not present, just ignore it. */
            if((bhf & 64) != 0) IgnoreVarint();
            /* Uncompressed size present. */
            /* Usually not present, just ignore it. */
            if((bhf & 128) != 0) IgnoreVarint();

            /* This is actually a varint, but it's shorter to read it as a byte. */
            if((0xFF & global->readCur[0]) != FILTER_ID_LZMA2) return SZ_ERROR_UNSUPPORTED_FILTER_ID;
            global->readCur = global->readCur + 1;

            /* This is actually a varint, but it's shorter to read it as a byte. */
            if((0xFF & global->readCur[0]) != 1) return SZ_ERROR_UNSUPPORTED_FILTER_PROPERTIES_SIZE;
            global->readCur = global->readCur + 1;

            dicSizeProp = 0xFF & global->readCur[0];
            global->readCur = global->readCur + 1;

            /* Typical large dictionary sizes:
             * 35: 805306368 bytes == 768 MiB
             * 36: 1073741824 bytes == 1 GiB
             * 37: 1610612736 bytes, largest supported by .xz
             * 38: 2147483648 bytes == 2 GiB
             * 39: 3221225472 bytes == 3 GiB
             * 40: 4294967295 bytes, largest supported by .7z
             */
            if(dicSizeProp > 40) return SZ_ERROR_BAD_DICTIONARY_SIZE;

            /* LZMA2 and .xz support it, we don't (for simpler memory management on
             * 32-bit systems).
             */
            if(dicSizeProp > MAX_DIC_SIZE_PROP) return SZ_ERROR_UNSUPPORTED_DICTIONARY_SIZE;

            /* Works if dicSizeProp <= 39. */
            global->dicSize = ((2 | ((dicSizeProp) & 1)) << ((dicSizeProp) / 2 + 11));
            /* TODO(pts): Free dic after use, also after realloc error. */
            require(global->dicSize >= LZMA_DIC_MIN, "global->dicSize >= LZMA_DIC_MIN");
            GrowCapacity(global->dicSize + MAX_MATCH_SIZE + sizeof_writeBuf);
            bhs2 = global->readCur - readAtBlock + 5;

            if(bhs2 > bhs) return SZ_ERROR_BLOCK_HEADER_TOO_LONG;

            result = IgnoreZeroBytes(bhs - bhs2);
            if(result != 0) return result;

            /* Ignore CRC32. */
            global->readCur = global->readCur + 4;
            /* Typically it's offset 24, xz creates it by default, minimal. */

            /* Finally Parse LZMA2 stream. */
            InitDecode();

            while(TRUE)
            {
                require(global->dicfPos == global->dicfLimit, "global->dicfPos == global->dicfLimit");

                /* Actually 2 bytes is enough to get to the index if everything is
                 * aligned and there is no block checksum.
                 */
                if(Preread(6) < 6) return SZ_ERROR_INPUT_EOF;
                control = 0xFF & global->readCur[0];

                if(control == 0)
                {
                    global->readCur = global->readCur + 1;
                    break;
                }
                else if(((control - 3) & 0xFF) < 0x7D) return SZ_ERROR_BAD_CHUNK_CONTROL_BYTE;

                us = ((0xFF & global->readCur[1]) << 8) + (0xFF & global->readCur[2]) + 1;

                /* Uncompressed chunk. */
                if(control < 3)
                {
                    /* assume it was already setup */
                    initDic = FALSE;
                    cs = us;
                    global->readCur = global->readCur + 3;
                    blockSizePad = blockSizePad - 3;

                    /* now test that assumption */
                    if(control == 1)
                    {
                        global->needInitProp = global->needInitState;
                        global->needInitState = TRUE;
                        global->needInitDic = FALSE;
                    }
                    else if(global->needInitDic) return SZ_ERROR_DATA;

                    LzmaDec_InitDicAndState(initDic, FALSE);
                }
                else
                {
                    /* LZMA chunk. */
                    mode = (((control) >> 5) & 3);
                    if(mode == 3) initDic = TRUE;
                    else initDic = FALSE;

                    if(mode > 0) initState = TRUE;
                    else initState = FALSE;

                    if((control & 64) != 0) isProp = TRUE;
                    else isProp = FALSE;

                    us = us + ((control & 31) << 16);
                    cs = ((0xFF & global->readCur[3]) << 8) + (0xFF & global->readCur[4]) + 1;

                    if(isProp)
                    {
                        result = InitProp(0xFF & global->readCur[5]);
                        if(result != 0) return result;

                        global->readCur = global->readCur + 1;
                        blockSizePad = blockSizePad - 1;
                    }
                    else if(global->needInitProp) return SZ_ERROR_MISSING_INITPROP;

                    global->readCur = global->readCur + 5;
                    blockSizePad = blockSizePad - 5;

                    if((!initDic && global->needInitDic) || (!initState && global->needInitState))
                    {
                        return SZ_ERROR_DATA;
                    }

                    LzmaDec_InitDicAndState(initDic, initState);
                    global->needInitDic = FALSE;
                    global->needInitState = FALSE;
                }

                require(us <= (1 << 24), "us <= (1 << 24)");
                require(cs <= (1 << 16), "cs <= (1 << 16)");
                require(global->dicfPos == global->dicfLimit, "global->dicfPos == global->dicfLimit");
                FlushDiscardOldFromStartOfDic();
                global->dicfLimit = global->dicfLimit + us;

                if(global->dicfLimit < us) return SZ_ERROR_MEM;

                /* Read 6 extra bytes to optimize away a read(...) system call in
                 * the Prefetch(6) call in the next chunk header.
                 */
                if(Preread(cs + 6) < cs) return SZ_ERROR_INPUT_EOF;

                /* Uncompressed chunk, at most 64 KiB. */
                if(control < 3)
                {
                    require((global->dicfPos + us) == global->dicfLimit, "global->dicfPos + us == global->dicfLimit");
                    FlushDiscardGrowDic(us);
                    memcpy(global->dicf + global->dicfPos, global->readCur, us);
                    global->dicfPos = global->dicfPos + us;

                    if((global->checkDicSize == 0) && ((global->dicSize - global->processedPos) <= us))
                    {
                        global->checkDicSize = global->dicSize;
                    }

                    global->processedPos = global->processedPos + us;
                }
                else
                {
                    /* Compressed chunk. */
                    /* This call doesn't change global->dicfLimit. */
                    result = LzmaDec_DecodeToDic(global->readCur, cs);

                    if(result != 0) return result;
                }

                if(global->dicfPos != global->dicfLimit) return SZ_ERROR_BAD_DICPOS;

                global->readCur = global->readCur + cs;
                blockSizePad = blockSizePad - cs;
                /* We can't discard decompressbuf[:global->dicfLimit] now,
                 * because we need it a dictionary in which subsequent calls to
                 * Lzma2Dec_DecodeToDic will look up backreferences.
                 */
            }

            Flush();
            /* End of LZMA2 stream. */

            /* End of block. */
            /* 7 for padding4 and CRC32 + 12 for the next block header + 6 for the next
             * chunk header.
             */
            if(Preread(7 + 12 + 6) < 7 + 12 + 6) return SZ_ERROR_INPUT_EOF;
            /* Ignore block padding. */
            result = (IgnoreZeroBytes(blockSizePad & 3));
            if(result != 0) return result;

            global->readCur = global->readCur + checksumSize;  /* Ignore CRC32, CRC64 etc. */
        }

        /* Look for another concatenated stream */

        /* 12 for the stream header + 12 for the first block header + 6 for the
         * first chunk header. empty.xz is 32 bytes.
         */
        if(Preread(12 + 12 + 6) < 12 + 12 + 6)
        {
            break;
        }

        if(0 != memcmp(global->readCur, "\xFD""7zXZ\0", 7)) {
            break;
        }
    }

    /* The .xz input file continues with the index, which we ignore from here. */
    return SZ_OK;
}

int main(int argc, char **argv)
{
    uint32_t res;
    char* name;
    char* dest;
    FUZZING = FALSE;
    name = NULL;
    dest = NULL;
    pos = 0;

    /* process arguments */
    int i = 1;
    while (i < argc)
    {
        if(NULL == argv[i])
        {
            i = i + 1;
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            name = argv[i+1];
            require(NULL != name, "the --file option requires a filename to be given\n");
            i = i + 2;
        }
        else if(match(argv[i], "-o") || match(argv[i], "--output"))
        {
            dest = argv[i+1];
            require(NULL != dest, "the --output option requires a filename to be given\n");
            i = i + 2;
        }
        else if(match(argv[i], "--chaos") || match(argv[i], "--fuzz-mode") || match(argv[i], "--fuzzing"))
        {
            FUZZING = TRUE;
            fputs("fuzz-mode enabled, preparing for chaos\n", stderr);
            i = i + 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" [--file $input.xz or --file $input.lzma] (or it'll read from stdin)\n", stderr);
            fputs(" [--output $output] (or it'll write to stdout)\n", stderr);
            fputs("--help to get this message\n", stderr);
            fputs("--fuzz-mode if you wish to fuzz this application safely\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option:", stderr);
            fputs(argv[i], stderr);
            fputs("\nAborting to avoid problems\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    if(NULL != name) source = fopen(name, "r");
    else source = stdin;
    if(NULL != dest) destination = fopen(dest, "w");
    else destination = stdout;

    if(FUZZING) destination = fopen("/dev/null", "w");
    global = calloc(1, sizeof(struct CLzmaDec));
    global->readBuf = calloc(sizeof_readBuf, sizeof(uint8_t));
    global->readCur = global->readBuf;
    global->readEnd = global->readBuf;
    global->allocCapacity = 0;
    global->dicSize = 0;
    res = DecompressXzOrLzma();
    free(global->dicf);  /* Pacify valgrind(1). */
    free(global->readBuf);
    free(global);
    return res;
}

File /M2libc/stdint.h

Live-bootstrap source file is 'seed/stage0-posix/M2libc/stdint.h'.
URL: https://github.com/oriansj/M2libc/blob/ddbb23567190e1e787305a545b81996be82b9cf9/stdint.h
/* Copyright (C) 2024 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _STDINT_H
#define _STDINT_H

#ifdef __M2__
/* nothing needed for M2-Planet as the standard types are included by default*/
#else
/* if we plan on supporting other compilers put stuff here */
#endif
#endif

File /mescc-tools-extra/catm.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools-extra/catm.c'.
URL: https://github.com/oriansj/mescc-tools-extra/blob/460648be66c4b4e4eeb138b8b88d5d6e0077e79e/catm.c
/* Copyright (C) 2019 Jeremiah Orians
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

#define BUFFER_SIZE 4096

/********************************************************************************
 * the reason why we are using read and write instead of fread and fwrite is    *
 * because it is much faster and involves less copying of values around         *
 ********************************************************************************/
int main(int argc, char** argv)
{
    if(2 > argc)
    {
        fputs("catm requires 2 or more arguments\n", stderr);
        exit(EXIT_FAILURE);
    }

    /* create a new file with read/write permissions only */
    int output = open(argv[1], O_TRUNC | O_CREAT | O_WRONLY , 0600);
    if(-1 == output)
    {
        fputs("The file: ", stderr);
        fputs(argv[1], stderr);
        fputs(" is not a valid output file name\n", stderr);
        exit(EXIT_FAILURE);
    }

    int i;
    int bytes;
    char* buffer = calloc(BUFFER_SIZE + 1, sizeof(char));
    int input;
    for(i = 2; i < argc ; i =  i + 1)
    {
        input = open(argv[i], 0, 0);
        if(-1 == input)
        {
            fputs("The file: ", stderr);
            fputs(argv[i], stderr);
            fputs(" is not a valid input file name\n", stderr);
            exit(EXIT_FAILURE);
        }
keep:
        bytes = read(input, buffer, BUFFER_SIZE);
        write(output, buffer, bytes);
        if(BUFFER_SIZE == bytes) goto keep;
    }

    free(buffer);
    return EXIT_SUCCESS;
}

File /mescc-tools-extra/cp.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools-extra/cp.c'.
URL: https://github.com/oriansj/mescc-tools-extra/blob/460648be66c4b4e4eeb138b8b88d5d6e0077e79e/cp.c
/* Copyright (C) 2020 fosslinux
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "M2libc/bootstrappable.h"

#define MAX_STRING 4096
#define MAX_ARRAY 256

/* Globals */
int verbose;

/* UTILITY FUNCTIONS */

/* Function to find a character's position in a string (last match) */
int find_last_char_pos(char* string, char a)
{
    int i = strlen(string) - 1;
    if(i < 0) return i;
    while(i >= 0)
    {
        /*
         * This conditional should be in the while conditional but we are
         * running into the M2-Planet short-circuit bug.
         */
        if(a == string[i]) break;
        i = i - 1;
    }
    return i;
}

/* Function to find the length of a char**; an array of strings */
int array_length(char** array)
{
    int length = 0;
    while(array[length] != NULL)
    {
        length = length + 1;
    }
    return length;
}

/* PROCESSING FUNCTIONS */

char* directory_dest(char* dest, char* source, int require_directory)
{
    /*
     * First, check if it is a directory to copy to.
     * We have two ways of knowing this:
     * - If the destination ends in a slash, the user has explicitly said
     *   it is a directory.
     * - Normally we would use stat() but we don't want to force support for
     *   that syscall onto the kernel, so we just attempt to chdir() into it
     *   and if it works then it must be a directory. A bit hacky, bit it
     *   works.
     */
    int isdirectory = FALSE;
    if(dest[strlen(dest) - 1] == '/')
    {
        isdirectory = TRUE;
    }
    if(!isdirectory)
    { /* Use the other testing method */
        /*
         * Get the current path so that we can chdir back to it if it does
         * chdir successfully.
         */
        char* current_path = calloc(MAX_STRING, sizeof(char));
        require(current_path != NULL, "Memory initialization of current_path in directory_dest failed\n");
        getcwd(current_path, MAX_STRING);
        require(!match("", current_path), "getcwd() failed\n");
        /*
         * chdir expects an absolute path.
         * If the first character is / then it is already absolute, otherwise
         * it is relative and needs to be changed (by appending current_path
         * to the dest path).
         */
        char* chdir_dest = calloc(MAX_STRING, sizeof(char));
        require(chdir_dest != NULL, "Memory initialization of chdir_dest in directory_dest failed\n");
        if(dest[0] != '/')
        { /* The path is relative, append current_path */
            strcat(chdir_dest, current_path);
            strcat(chdir_dest, "/");
            strcat(chdir_dest, dest);
        }
        else
        { /* The path is absolute */
            strcpy(chdir_dest, dest);
        }
        if(0 <= chdir(chdir_dest))
        { /* chdir returned successfully */
            /*
             * But because of M2-Planet, that doesn't mean anything actually
             * happened, check that before we go any further.
             */
            char* new_path = calloc(MAX_STRING, sizeof(char));
            require(new_path != NULL, "Memory initialization of new_path in directory_dest failed\n");
            getcwd(new_path, MAX_STRING);
            if(!match(current_path, new_path))
            {
                isdirectory = TRUE;
                chdir(current_path);
            }
        }
        free(chdir_dest);
        free(current_path);
    }

    /*
     * If it isn't a directory, and we require one, error out.
     * Otherwise, just return what we were given, we're done here.
     */
    if(require_directory) require(isdirectory, "Provide a directory destination for multiple source files\n");
    if(!isdirectory) return dest;

    /* If it is, we need to make dest a full path */
    /* 1. Get the basename of source */
    char* basename = calloc(MAX_STRING, sizeof(char));
    require(basename != NULL, "Memory initialization of basename in directory_dest failed\n");
    int last_slash_pos = find_last_char_pos(source, '/');
    if(last_slash_pos >= 0)
    { /* Yes, there is a slash in it, copy over everything after that pos */
        unsigned spos; /* source pos */
        unsigned bpos = 0; /* basename pos */
        /* Do the actual copy */
        for(spos = last_slash_pos + 1; spos < strlen(source); spos = spos + 1)
        {
            basename[bpos] = source[spos];
            bpos = bpos + 1;
        }
    }
    else
    { /* No, there is no slash in it, hence the basename is just the source */
        strcpy(basename, source);
    }
    /* 2. Ensure our dest (which is a directory) has a trailing slash */
    if(dest[strlen(dest) - 1] != '/')
    {
        strcat(dest, "/");
    }
    /* 3. Add the basename to the end of the directory */
    strcat(dest, basename);
    free(basename);

    /* Now we have a returnable path! */
    return dest;
}

void copy_file(char* source, char* dest)
{
    if(verbose)
    { /* Output message */
        /* Of the form 'source' -> 'dest' */
        fputs("'", stdout);
        fputs(source, stdout);
        fputs("' -> '", stdout);
        fputs(dest, stdout);
        fputs("'\n", stdout);
    }

    /* Open source and dest as FILE*s */
    FILE* fsource = fopen(source, "r");
    if(fsource == NULL)
    {
        fputs("Error opening source file ", stderr);
        fputs(source, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }
    FILE* fdest = fopen(dest, "w");
    if(fdest < 0)
    {
        fputs("Error opening destination file", stderr);
        fputs(dest, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }

    /*
     * The following loop reads a character from the source and writes it to the
     * dest file. This is all M2-Planet supports.
     */
    int c = fgetc(fsource);
    while(c != EOF)
    {
        fputc(c, fdest);
        c = fgetc(fsource);
    }

    /* Cleanup */
    fclose(fsource);
    fclose(fdest);
}

int main(int argc, char** argv)
{
    /* Initialize variables */
    char** sources = calloc(MAX_ARRAY, sizeof(char*));
    require(sources != NULL, "Memory initialization of sources failed\n");
    int sources_index = 0;
    char* dest = NULL;

    /* Set defaults */
    verbose = FALSE;

    int i = 1;
    int j;
    int args_found;
    /* Loop arguments */
    while(i <= argc)
    {
        if(NULL == argv[i])
        { /* Ignore and continue */
            i = i + 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs("Usage: ", stdout);
            fputs(argv[0], stdout);
            fputs(" [-h | --help] [-V | --version] [-v | --verbose] source1 source2 sourcen destination\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-V") || match(argv[i], "--version"))
        { /* Output version */
            fputs("cp version 1.3.0\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-v") || match(argv[i], "--verbose"))
        {
            verbose = TRUE;
            i = i + 1;
        }
        else if(argv[i][0] != '-')
        { /* It is not an option */
            /*
             * We can tell if this is the source file or the destination file
             * through looking *ahead*. If it is the last of this type of argument then
             * it must be the destination. (1 destination, many sources).
             */
            j = i + 1;
            args_found = 0;
            while(j < array_length(argv))
            {
                if(argv[j][0] != '-')
                { /* It's one of these type of arguments */
                    args_found = args_found + 1;
                }
                j = j + 1;
            }
            if(args_found == 0)
            { /* We are setting the destination (there are no more left after this) */
                dest = calloc(MAX_STRING, sizeof(char));
                require(dest != NULL, "Memory initialization of dest failed\n");
                strcpy(dest, argv[i]);
            }
            else
            { /* We are setting a source */
                require(sources_index < MAX_ARRAY, "Too many files\n");
                sources[sources_index] = calloc(MAX_STRING, sizeof(char));
                require(sources[sources_index] != NULL, "Memory initialization of sources[source_index] failed\n");
                strcpy(sources[sources_index], argv[i]);
                sources_index = sources_index + 1;
            }
            i = i + 1;
        }
        else
        { /* Unknown argument */
            fputs("UNKNOWN_ARGUMENT\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    /* Sanitize values */
    /* Ensure the two values have values */
    /* Another workaround for short-circuit bug */
    int error = FALSE;
    if(sources[0] == NULL) error = TRUE;
    if(error == FALSE) if(match(sources[0], "")) error = TRUE;
    require(!error, "Provide a source file\n");
    error = FALSE;
    if(dest == NULL) error = TRUE;
    if(error == FALSE) if(match(dest, "")) error = TRUE;
    require(!error, "Provide a destination file\n");

    /* Loop through all of the sources, copying each one */
    char* this_dest;
    for(i = 0; i < array_length(sources); i = i + 1)
    {
        /* Convert the dest variable to a full path if it's a directory copying to */
        /*
         * Also, if there is more than one source, we have to be copying to
         * a directory destination...
         */
        if(array_length(sources) == 1)
        {
            dest = directory_dest(dest, sources[i], FALSE);
            copy_file(sources[i], dest);
        }
        else
        {
            this_dest = calloc(MAX_STRING, sizeof(char));
            require(this_dest != NULL, "Memory initalization of this_dest failed\n");
            this_dest = directory_dest(dest, sources[i], TRUE);
            copy_file(sources[i], this_dest);
        }
        /* Perform the actual copy */
        free(sources[i]);
    }

    free(sources);
    free(dest);

    return EXIT_SUCCESS;
}

File /mescc-tools-extra/chmod.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools-extra/chmod.c'.
URL: https://github.com/oriansj/mescc-tools-extra/blob/460648be66c4b4e4eeb138b8b88d5d6e0077e79e/chmod.c
/* Copyright (C) 2020 fosslinux
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "M2libc/bootstrappable.h"

/* Define all of the constants */
#define MAX_STRING 4096
#define MAX_ARRAY 256

struct files
{
    char* name;
    struct files* next;
};

/* Globals */
int verbose;

/* PROCESSING FUNCTIONS */
int main(int argc, char** argv)
{
    /* Initialize variables */
    char* mode = NULL;
    struct files* f = NULL;
    struct files* n;
    int ok;

    /* Set defaults */
    verbose = FALSE;

    int i = 1;
    /* Loop arguments */
    while(i <= argc)
    {
        if(NULL == argv[i])
        { /* Ignore and continue */
            i = i + 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs("Usage: ", stdout);
            fputs(argv[0], stdout);
            fputs(" [-h | --help] [-V | --version] [-v | --verbose]\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-V") || match(argv[i], "--version"))
        { /* Output version */
            fputs("chmod version 1.3.0\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-v") || match(argv[i], "--verbose"))
        {
            verbose = TRUE;
            i = i + 1;
        }
        else
        { /* It must be the file or the mode */
            if(mode == NULL)
            { /* Mode always comes first */
                mode = calloc(MAX_STRING, sizeof(char));
                require(mode != NULL, "Memory initialization of mode failed\n");
                /* We need to indicate it is octal */
                strcat(mode, "0");
                strcat(mode, argv[i]);
            }
            else
            { /* It's a file, as the mode is already done */
                n = calloc(1, sizeof(struct files));
                require(n != NULL, "Memory initialization of files failed\n");
                n->next = f;
                f = n;
                f->name = argv[i];
            }
            i = i + 1;
        }
    }

    /* Ensure the two values have values */
    require(mode != NULL, "Provide a mode\n");
    require(f != NULL, "Provide a file\n");

    /* Convert the mode str into octal */
    int omode = strtoint(mode);

    /* Loop over files to be operated on */
    while(NULL != f)
    {
        /* Make sure the file can be opened */
        ok = access(f->name, 0);
        if(ok != 0)
        {
            fputs("The file: ", stderr);
            fputs(f->name, stderr);
            fputs(" does not exist\n", stderr);
            exit(EXIT_FAILURE);
        }

        /* Verbose message */
        if(verbose)
        {
            fputs("mode of '", stdout);
            fputs(f->name, stdout);
            fputs("' changed to ", stdout);
            fputs(mode, stdout);
            fputs("\n", stdout);
        }

        /* Perform the chmod */
        chmod(f->name, omode);
        f = f->next;
    }
}

File /mescc-tools-extra/rm.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools-extra/rm.c'.
URL: https://github.com/oriansj/mescc-tools-extra/blob/460648be66c4b4e4eeb138b8b88d5d6e0077e79e/rm.c
/* Copyright (C) 2021 Jeremiah Orians
 * This file is part of mescc-tools-extra
 *
 * mescc-tools-extra is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools-extra is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools-extra.  If not, see <http://www.gnu.org/licenses/>.
 */

/********************************************************************************
 * "rm" can be used to delete files. It can also delete                         *
 * parent directories.                                                          *
 *                                                                              *
 * Usage: rm <dir1>/<file1> <file2>                                             *
 *                                                                              *
 * These are all highly standard and portable headers.                          *
 ********************************************************************************/
#include <stdio.h>
#include <string.h>

/* This is for unlink() ; this may need to be changed for some platforms. */
#include <unistd.h>  /* For unlink() */
#include <stdlib.h>
#include "M2libc/bootstrappable.h"

void delete_dir(char* name)
{
    int r = unlink(name);
    if(0 != r)
    {
        fputs("unable to delete file: ", stderr);
        fputs(name, stderr);
        fputs(" !!!\n", stderr);
    }
}

int main(int argc, char **argv)
{
    int i;
    for(i = 1; argc > i; i = i + 1)
    {
        delete_dir(argv[i]);
    }

    return 0;
}

File /mescc-tools-extra/replace.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools-extra/replace.c'.
URL: https://github.com/oriansj/mescc-tools-extra/blob/460648be66c4b4e4eeb138b8b88d5d6e0077e79e/replace.c
/* Copyright (C) 2019 Jeremiah Orians
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "M2libc/bootstrappable.h"

char* input_name;
FILE* input;
char* output_name;
FILE* output;
char* pattern;
size_t pattern_length;
char* replacement;
char* buffer;
size_t buffer_index;
char* hold;

void read_next_byte()
{
    int c= hold[0];
    size_t i = 0;
    while(i < pattern_length)
    {
        hold[i] = hold[i+1];
        i = i + 1;
    }

    hold[pattern_length-1] = buffer[buffer_index];
    buffer_index = buffer_index + 1;

    /* NEVER WRITE NULLS!!! */
    if(0 != c) fputc(c, output);
}

void clear_hold()
{
    /* FILL hold with NULLS */
    size_t i = 0;
    while(i < pattern_length)
    {
        hold[i] = 0;
        i = i + 1;
    }
}

void check_match()
{
    /* Do the actual replacing */
    if(match(pattern, hold))
    {
        fputs(replacement, output);
        clear_hold();
    }
}

int main(int argc, char** argv)
{
    output_name = "/dev/stdout";
    pattern = NULL;
    replacement = NULL;
    buffer_index = 0;

    int i = 1;
    while (i < argc)
    {
        if(NULL == argv[i])
        {
            i = i + 1;
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            input_name = argv[i+1];
            require(NULL != input_name, "the --file option requires a filename to be given\n");
            i = i + 2;
        }
        else if(match(argv[i], "-o") || match(argv[i], "--output"))
        {
            output_name = argv[i+1];
            require(NULL != output_name, "the --output option requires a filename to be given\n");
            i = i + 2;
        }
        else if(match(argv[i], "-m") || match(argv[i], "--match-on"))
        {
            pattern = argv[i+1];
            require(NULL != pattern, "the --match-on option requires a string to be given\n");
            i = i + 2;
        }
        else if(match(argv[i], "-r") || match(argv[i], "--replace-with"))
        {
            replacement = argv[i+1];
            require(NULL != replacement, "the --replace-with option requires a string to be given\n");
            i = i + 2;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" --file $input", stderr);
            fputs(" --match-on $string", stderr);
            fputs(" --replace-with $string", stderr);
            fputs(" [--output $output] (or it'll dump to stdout)\n", stderr);
            fputs("--help to get this message\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option:", stderr);
            fputs(argv[i], stderr);
            fputs("\nAborting to avoid problems\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    /* Sanity check that we got everything we need */
    require(NULL != input_name, "You need to pass an input file with --file\n");
    require(NULL != output_name, "You need to pass an output file with --output\n");
    require(NULL != pattern, "You can't do a replacement without something to match on\n");
    require(NULL != replacement, "You can't do a replacement without something to replace it with\n");

    input = fopen(input_name, "r");
    require(NULL != input, "unable to open requested input file!\n");

    /* Get enough buffer to read it all */
    fseek(input, 0, SEEK_END);
    size_t size = ftell(input);
    buffer = malloc((size + 8) * sizeof(char));

    /* Save ourself work if the input file is too small */
    pattern_length = strlen(pattern);
    require(pattern_length < size, "input file is to small for pattern\n");

    /* Now read it all into buffer */
    fseek(input, 0, SEEK_SET);
    size_t r = fread(buffer,sizeof(char), size, input);
    require(r == size, "incomplete read of input\n");
    fclose(input);

    /* Now we can safely open the output (which could have been the same as the input */
    output = fopen(output_name, "w");
    require(NULL != input, "unable to open requested output file!\n");

    /* build our match buffer */
    hold = calloc(pattern_length + 4, sizeof(char));
    require(NULL != hold, "temp memory allocation failed\n");

    /* Replace it all */
    while((size + pattern_length + 4) >= buffer_index)
    {
        read_next_byte();
        check_match();
    }
    fclose(output);
}

File /mescc-tools-extra/wrap.c

Live-bootstrap source file is 'seed/stage0-posix/mescc-tools-extra/wrap.c'.
URL: https://github.com/oriansj/mescc-tools-extra/blob/460648be66c4b4e4eeb138b8b88d5d6e0077e79e/wrap.c
/* SPDX-FileCopyrightText: 2023 Max Hearnden <max@hearnden.org.uk> */
/* SPDX-License-Identifier: GPL-3.0-or-later */


#define CLONE_NEWUSER 0x10000000
#define CLONE_NEWNS 0x00020000
#define MS_BIND 4096
#define MS_REC 16384
#define MNT_DETACH 0x00000002

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#include "M2libc/bootstrappable.h"

void touch(char *path)
{
    int fd = open(path, O_CREAT, 0777);
    if (fd == -1)
    {
        fputs("Failed to create file ", stderr);
        fputs(path, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }
    if (close(fd) != 0)
    {
        fputs("Failed to close file ", stderr);
        fputs(path, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }
}

void mkmount(char *source, char *target, char *filesystemtype, unsigned mountflags, void *data, int type)
{
    int r = 0;
    if (type)
    {
        r = mkdir(target, 0755);
    }
    else
    {
        touch(target);
    }
    if (r != 0 && r != -17)
    {
        fputs("Failed to create mountpoint ", stderr);
        fputs(target, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }

    r = mount(source, target, filesystemtype, mountflags, data);

    if (r != 0)
    {
        fputs("Failed to mount directory ", stderr);
        fputs(target, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }
}

void set_map(int parent_id, char *path) {
    int fd = open(path, O_WRONLY, 0);
    if (fd == -1)
    {
        fputs("Failed to open map file ", stderr);
        fputs(path, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }

    char *map_contents = calloc(38, sizeof(char));

#ifdef __M2__
    strcpy(map_contents, "0 ");
    char *parent_id_str = int2str(parent_id, 10, 0);
    strcat(map_contents, parent_id_str);
    strcat(map_contents, " 1");
#else
    snprintf(map_contents, 38, "0 %i 1", parent_id);
#endif
    write(fd, map_contents, strlen(map_contents));
    write(STDOUT_FILENO, map_contents, strlen(map_contents));
    free(map_contents);
    close(fd);
}

void deny_setgroups() {
    int fd = open("/proc/self/setgroups", O_WRONLY, 0777);
    if(fd == -1)
    {
        fputs("Failed to open /proc/self/setgroups\n", stderr);
        exit(EXIT_FAILURE);
    }
    write(fd, "deny", 4);
    close(fd);
}

char **copy_environment(char **newenv, char *variable) {
    char *var_contents = getenv(variable);
    size_t var_len = strlen(variable);
    if (var_contents != NULL)
    {
        *newenv = malloc(var_len + 2 + strlen(var_contents));
        if (newenv[0] == NULL)
        {
            fputs("Failed to allocate space for new environment\n", stderr);
            exit(EXIT_FAILURE);
        }
        memcpy(*newenv, variable, var_len);
        (*newenv)[var_len] = '=';
        strcpy(*newenv + var_len + 1, var_contents);
#ifdef __M2__
        return newenv + sizeof(char *);
#else
        return newenv + 1;
#endif
    }
    return newenv;
}

int main(int argc, char **argv)
{
    if(argc <= 1)
    {
        fputs("Expected at least one argument: command\n", stderr);
        exit(EXIT_FAILURE);
    }
    char *cwd = get_current_dir_name();
    /* Do nothing if cwd is already root */
    if (strcmp(cwd, "/"))
    {
        int uid = geteuid();
        int gid = getegid();
        /* Don't create a user and mount namespace if we are already root */
        if (uid != 0)
        {
            /* CLONE_NEWUSER allows for CLONE_NEWNS in an unprivileged process */
            if (unshare(CLONE_NEWUSER | CLONE_NEWNS) != 0) {
                fputs("Failed to create user and mount namespaces\n", stderr);
                exit(EXIT_FAILURE);
            }
            /* Prevent the use of setgroups and make gid_map writeable */
            deny_setgroups();
            /* Map the root user in the user namespace to our user id */
            set_map(uid, "/proc/self/uid_map");
            /* Map the root group in the user namespace to our group id */
            set_map(gid, "/proc/self/gid_map");
        }
        int r = mkdir("dev", 0755);
        if (r != 0 && r != -17)
        {
            fputs("Failed to create dev folder\n", stderr);
            exit(EXIT_FAILURE);
        }
#if !__uefi__
        mkmount ("/dev/null", "dev/null", "", MS_BIND, NULL, 0);
        mkmount ("/dev/zero", "dev/zero", "", MS_BIND, NULL, 0);
        mkmount ("/dev/random", "dev/random", "", MS_BIND, NULL, 0);
        mkmount ("/dev/urandom", "dev/urandom", "", MS_BIND, NULL, 0);
        mkmount ("/dev/ptmx", "dev/ptmx", "", MS_BIND, NULL, 0);
        mkmount ("/dev/tty", "dev/tty", "", MS_BIND, NULL, 0);
        mkmount ("tmpfs", "dev/shm", "tmpfs", 0, NULL, 1);
        mkmount ("/proc", "proc", "", MS_BIND | MS_REC, NULL, 1);
        mkmount ("/sys", "sys", "", MS_BIND | MS_REC, NULL, 1);
        mkmount ("tmpfs", "tmp", "tmpfs", 0, NULL, 1);
#endif
        if (chroot (".") != 0)
        {
            fputs("Failed to chroot into .\n", stderr);
            exit(EXIT_FAILURE);
        }
    }
    free(cwd);


    /* Copy environment variables into the new envornment */
    char **newenv = malloc(13 * sizeof(char *));
    char **newenv_end = newenv;
    if (newenv == NULL)
    {
        fputs("Failed to allocate space for new environment\n", stderr);
        exit(EXIT_FAILURE);
    }

    newenv_end = copy_environment(newenv_end, "ARCH");
    newenv_end = copy_environment(newenv_end, "ARCH_DIR");
    newenv_end = copy_environment(newenv_end, "M2LIBC");
    newenv_end = copy_environment(newenv_end, "TOOLS");
    newenv_end = copy_environment(newenv_end, "BLOOD_FLAG");
    newenv_end = copy_environment(newenv_end, "BASE_ADDRESS");
    newenv_end = copy_environment(newenv_end, "ENDIAN_FLAG");
    newenv_end = copy_environment(newenv_end, "BINDIR");
    newenv_end = copy_environment(newenv_end, "BUILDDIR");
    newenv_end = copy_environment(newenv_end, "TMPDIR");
    newenv_end = copy_environment(newenv_end, "OPERATING_SYSTEM");
    newenv_end[0] = "WRAPPED=yes";
    newenv_end[1] = NULL;


#ifdef __M2__
#if __uefi__
    return spawn (argv[1], argv + sizeof(char *), newenv);
#else
    return execve (argv[1], argv + sizeof(char *), newenv);
#endif
#else
    return execve (argv[1], argv + 1, newenv);
#endif
}

File /x86.answers

Live-bootstrap source file is 'seed/stage0-posix/x86.answers'.
URL: https://github.com/oriansj/stage0-posix/blob/779e5424d4b55fe9b7faea2285ae8b6486df0433/x86.answers
ad9b2aa64a142003a80174499be5acf9c496b3671f7e0cc94fb25c96a51d5cc8  x86/bin/blood-elf
c1b7a68c82bffb0986aae773e0fbc13da1ff97f858339ab67ff7ffd65c8216e5  x86/bin/catm
0fa861e01c90797c58b0cde968d0f73bc785f9033d9a29f8eeb12581079b3c89  x86/bin/chmod
faeec933084e900af0980a260f896f2880fa43ce0d949bd58472b301ac21965a  x86/bin/cp
d296d7cdd798fd1397b0f72c9f9f34c86cd485f1bdd3396e08dda46637e2cbac  x86/bin/get_machine
841b01c6277b0733f06327d5a09b4bb0b6ca88f5275dd67750387f5b255b0aa9  x86/bin/hex2
a6d4d5e422c8d12184a095082099b5ccb3176fc7cdc05f6c780b8d82bb6db927  x86/bin/kaem
fbde69a7c9aef99199a8bd44b89cbc0e56fbe546114f598443690d347e4a5648  x86/bin/M1
f2569d58b6eb223aa9319467f1b51bb0372fe505bb215d8977a3eb5d9e9b3d26  x86/bin/M2-Mesoplanet
ce987ec136baa1fe983be99dc354bce816ae5b6813a33526a8fd8b1198a38882  x86/bin/M2-Planet
2aa85ef9084f50daf02de8735ff54ba7c4cfc94b958b72a228c463747e6d1d41  x86/bin/match
5992d06051a757dc8290a8c0b7e8e3472d9676ea0cf2c6d1056a67a69bdf7100  x86/bin/mkdir
f0c75f631b34a67172ec2fa836589bc51765f8a8b6b92d70e3231b9e2c329d42  x86/bin/replace
d9a7e2731da76b3ea3d9ed430f7bc7ba99677c1608a1d75175bea329f928b925  x86/bin/rm
502bd938c8e5fb4fbb6cb30f334c3ad4ffc82cf6fb6d145297f7f62a0955c7cd  x86/bin/sha256sum
6a5470b5963f906f9eddea18dafb0cdcb38d0ac65505dbb225de02b04e544d7c  x86/bin/ungz
5e9be87db52f97e6c5659c13b1b7f928ad84f920887e9b94315dd8f2ca803036  x86/bin/unbz2
7e2fb5aafa40aa65e427deec89d95c556a950dbcd7e0e1631edcddc035de9d30  x86/bin/unxz
30b303443863b44f56e914a5d6f8139923b44b43a1ffd83fcdd39dee2639a8a3  x86/bin/untar

File /after.kaem

Live-bootstrap source file is 'seed/after.kaem'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/seed/after.kaem
#!/bin/sh

# SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>
# SPDX-FileCopyrightText: 2021 Paul Dersey <pdersey@gmail.com>
# SPDX-FileCopyrightText: 2020-2022 fosslinux <fosslinux@aussies.space>
# SPDX-FileCopyrightText: 2022 Dor Askayo <dor.askayo@gmail.com>
#
# SPDX-License-Identifier: GPL-3.0-or-later

set -ex

PATH=/${ARCH_DIR}/bin

catm seed-full.kaem /steps/bootstrap.cfg /steps/env seed.kaem
kaem --file seed-full.kaem

File /steps/bootstrap.cfg

Source file is 'replacement/steps/bootstrap.cfg'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/bootstrap.cfg
ARCH=x86
ARCH_DIR=/x86
FORCE_TIMESTAMPS=False
CHROOT=True
UPDATE_CHECKSUMS=False
JOBS=1
SWAP_SIZE=0
FINAL_JOBS=1
INTERNAL_CI=False
INTERACTIVE=False
BARE_METAL=False
DISK=sda1
KERNEL_BOOTSTRAP=False
BUILD_KERNELS=False

File /steps/env

Live-bootstrap source file is 'steps/env'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/env
# SPDX-FileCopyrightText: 2023 fosslinux <fosslinux@aussies.space>
#
# SPDX-License-Identifier: GPL-3.0-or-later

DISTFILES=/external/distfiles
PREFIX=/usr
BINDIR=${PREFIX}/bin
LIBDIR=${PREFIX}/lib/mes
INCDIR=${PREFIX}/include/mes
SRCDIR=/steps
TMPDIR=/tmp
PATH=${BINDIR}:/${ARCH_DIR}/bin

File /seed.kaem

Live-bootstrap source file is 'seed/seed.kaem'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/seed/seed.kaem
#!/bin/sh

# SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>
# SPDX-FileCopyrightText: 2021 Paul Dersey <pdersey@gmail.com>
# SPDX-FileCopyrightText: 2020-2022 fosslinux <fosslinux@aussies.space>
# SPDX-FileCopyrightText: 2022 Dor Askayo <dor.askayo@gmail.com>
#
# SPDX-License-Identifier: GPL-3.0-or-later

set -ex

mkdir -p ${PREFIX} ${BINDIR} ${LIBDIR} ${INCDIR} ${SRCDIR} ${TMPDIR} /dev

# Temporarily change PATH
PATH=/${ARCH_DIR}/bin

# Remove remaining dependencies on /bin (stage0-posix directory)
cp /${ARCH_DIR}/bin/blood-elf ${BINDIR}/blood-elf
cp /${ARCH_DIR}/bin/catm ${BINDIR}/catm
cp /${ARCH_DIR}/bin/chmod ${BINDIR}/chmod
cp /${ARCH_DIR}/bin/get_machine ${BINDIR}/get_machine
cp /${ARCH_DIR}/bin/hex2 ${BINDIR}/hex2
cp /${ARCH_DIR}/bin/kaem ${BINDIR}/kaem
cp /${ARCH_DIR}/bin/match ${BINDIR}/match
cp /${ARCH_DIR}/bin/M1 ${BINDIR}/M1
cp /${ARCH_DIR}/bin/M2-Mesoplanet ${BINDIR}/M2-Mesoplanet
cp /${ARCH_DIR}/bin/M2-Planet ${BINDIR}/M2-Planet
cp /${ARCH_DIR}/bin/mkdir ${BINDIR}/mkdir
cp /${ARCH_DIR}/bin/sha256sum ${BINDIR}/sha256sum
cp /${ARCH_DIR}/bin/unbz2 ${BINDIR}/unbz2
cp /${ARCH_DIR}/bin/ungz ${BINDIR}/ungz
cp /${ARCH_DIR}/bin/untar ${BINDIR}/untar
cp /${ARCH_DIR}/bin/unxz ${BINDIR}/unxz
cp /${ARCH_DIR}/bin/cp ${BINDIR}/cp
cp /${ARCH_DIR}/bin/replace ${BINDIR}/replace
cp /${ARCH_DIR}/bin/rm ${BINDIR}/rm

chmod 755 ${BINDIR}/blood-elf
chmod 755 ${BINDIR}/catm
chmod 755 ${BINDIR}/chmod
chmod 755 ${BINDIR}/cp
chmod 755 ${BINDIR}/get_machine
chmod 755 ${BINDIR}/hex2
chmod 755 ${BINDIR}/kaem
chmod 755 ${BINDIR}/match
chmod 755 ${BINDIR}/M1
chmod 755 ${BINDIR}/M2-Mesoplanet
chmod 755 ${BINDIR}/M2-Planet
chmod 755 ${BINDIR}/mkdir
chmod 755 ${BINDIR}/sha256sum
chmod 755 ${BINDIR}/unbz2
chmod 755 ${BINDIR}/ungz
chmod 755 ${BINDIR}/untar
chmod 755 ${BINDIR}/unxz
chmod 755 ${BINDIR}/replace
chmod 755 ${BINDIR}/rm

PATH=${BINDIR}
M2LIBC_PATH=/M2libc

# mes envars
NYACC_PKG=nyacc-1.00.2
MES_PKG=mes-0.26
MES_PREFIX=${SRCDIR}/${MES_PKG}/build/${MES_PKG}
GUILE_LOAD_PATH=${MES_PREFIX}/mes/module:${MES_PREFIX}/module:${SRCDIR}/${MES_PKG}/build/${NYACC_PKG}/module

M2-Mesoplanet --architecture ${ARCH} -f script-generator.c -o script-generator
# Checksums
if match x${UPDATE_CHECKSUMS} xTrue; then
    sha256sum -o script-generator.${ARCH}.checksums script-generator
else
    sha256sum -c script-generator.${ARCH}.checksums
fi
./script-generator /steps/manifest
kaem --file /steps/0.sh

File /script-generator.c

Live-bootstrap source file is 'seed/script-generator.c'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/seed/script-generator.c
/*
 * SPDX-FileCopyrightText: 2023 fosslinux <fosslinux@aussies.space>
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

#define MAX_TOKEN 64
#define MAX_STRING 2048

#include <bootstrappable.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Token {
    char *val;
    struct Token *next;
};
typedef struct Token Token;

#define TYPE_BUILD 1
#define TYPE_IMPROVE 2
#define TYPE_DEFINE 3
#define TYPE_JUMP 4
#define TYPE_UNINSTALL 5

struct Directive {
    Token *tok;
    struct Directive *next;
    int type;
    char *arg; /* The primary argument */
};
typedef struct Directive Directive;

/* Tokenizer. */

/* Skip over a comment. */
char consume_comment(FILE *in) {
    /* Discard the rest of the line. */
    char c = fgetc(in);
    while (c != -1 && c != '\n')
        c = fgetc(in);
    return c;
}

char consume_line(FILE *in, Directive *directive) {
    char c = fgetc(in);

    /* Short-circuit if whole line is comment or blank line. */
    if (c == '#') {
        c = consume_comment(in);
        return c;
    } else if (c == '\n' || c == -1) {
        return c;
    }

    /* Ok, we will have something to put here. */
    directive->next = calloc(1, sizeof(Directive));
    directive = directive->next;

    Token *head = calloc(1, sizeof(Token));
    Token *cur = head;
    char *out;
    int i = 0;
    while (c != -1 && c != '\n') {
        /* Initialize next token. */
        cur->next = calloc(1, sizeof(Token));
        cur = cur->next;
        cur->val = calloc(MAX_TOKEN, sizeof(char));
        out = cur->val;
        /* Copy line to token until a space (or EOL/EOF) or comment is found. */
        while (c != -1 && c != '\n' && c != ' ' && c != '#') {
            out[0] = c;
            out += 1;
            c = fgetc(in);
        }
        /* Go to start of next token. */
        if (c == ' ') {
            c = fgetc(in);
        }
        /* Handle comment. */
        if (c == '#') {
            c = consume_comment(in);
        }
    }

    /* Add information to directive. */
    directive->tok = head->next;

    return c;
}

Directive *tokenizer(FILE *in) {
    Directive *head = calloc(1, sizeof(Directive));
    Directive *cur = head;

    char c;
    while (c != -1) {
        /*
         * Note that consume_line fills cur->next, not cur.
         * This avoids having an empty last Directive.
         */
        c = consume_line(in, cur);
        if (cur->next != NULL) {
            cur = cur->next;
        }
    }
    return head->next;
}

/* Config variables. */

struct Variable {
    char *name;
    char *val;
    struct Variable *next;
};
typedef struct Variable Variable;

Variable *variables;

Variable *load_config() {
    FILE *config = fopen("/steps/bootstrap.cfg", "r");
    /* File does not exist check. */
    if (config == NULL) {
        return NULL;
    }

    char *line = calloc(MAX_STRING, sizeof(char));
    Variable *head = calloc(1, sizeof(Variable));
    Variable *cur = head;
    /* For each line... */
    char *equals;
    while (fgets(line, MAX_STRING, config) != 0) {
        /* Weird M2-Planet behaviour. */
        if (*line == 0) {
            break;
        }
        cur->next = calloc(1, sizeof(Variable));
        cur = cur->next;
        /* Split on the equals. First half is name, second half is value. */
        equals = strchr(line, '=');
        if (equals == 0) {
            fputs("bootstrap.cfg should have the format var=val on each line.", stderr);
            exit(1);
        }
        cur->name = calloc(equals - line + 1, sizeof(char));
        strncpy(cur->name, line, equals - line);
        equals += 1;
        cur->val = calloc(strlen(equals), sizeof(char));
        strncpy(cur->val, equals, strlen(equals) - 1);
        line = calloc(MAX_STRING, sizeof(char));
    }
    variables = head->next;
    fclose(config);
}

void output_config(FILE *out) {
    Variable *variable;
    for (variable = variables; variable != NULL; variable = variable->next) {
        fputs(variable->name, out);
        fputs("=", out);
        fputs(variable->val, out);
        fputs("\n", out);
    }
}

char *get_var(char *name) {
    /* Search through existing variables. */
    Variable *var;
    Variable *last = NULL;
    for (var = variables; var != NULL; var = var->next) {
        if (strcmp(name, var->name) == 0) {
            return var->val;
        }
        last = var;
    }

    /* If the variable is unset, prompt the user. */
    if (variables == NULL) {
        variables = calloc(1, sizeof(Variable));
        var = variables;
    } else {
        last->next = calloc(1, sizeof(Variable));
        var = last->next;
    }
    var->name = calloc(strlen(name) + 1, sizeof(char));
    strcpy(var->name, name);
    var->val = calloc(MAX_STRING, sizeof(char));
    fputs("You have not set a value for ", stdout);
    fputs(name, stdout);
    fputs(" in bootstrap.cfg. Please set it now:\n", stdout);
    while (fgets(var->val, MAX_STRING, stdin) == 0 || var->val[0] == '\n') {
        fputs("Error inputting, try again:\n", stdout);
    }
    if (var->val[0] == 0) {
        fputs("You put in an EOF!\n", stderr);
        exit(1);
    }
    /* Trim the newline. */
    var->val[strlen(var->val)] = 0;
    return var->val;
}

/* Recursive descent interpreter. */

Token *fill(Token *tok, Directive *directive, int type) {
    directive->type = type;
    directive->arg = tok->val;
    return tok->next;
}

Token *logic(Token *tok, char **val) {
    /* logic = "("
     *   (name |
     *   (name "==" value) |
     *   (name "!=" value) |
     *   (logic "||" logic) |
     *   (logic "&&" logic))
     *   ")"
     */

    char *lhs = tok->val;
    char *rhs;
    tok = tok->next;
    if (strcmp(tok->val, ")") == 0) {
        /* Case where it's just a constant. */
        *val = lhs;
        return tok;
    } else if (strcmp(tok->val, "==") == 0) {
        /* Case for equality. */
        rhs = tok->next->val;
        tok = tok->next->next;
        if (strcmp(get_var(lhs), rhs) == 0) {
            lhs = "True";
        } else {
            lhs = "False";
        }
    } else if (strcmp(tok->val, "!=") == 0) {
        /* Case for inequality. */
        rhs = tok->next->val;
        tok = tok->next->next;
        if (strcmp(get_var(lhs), rhs) == 0) {
            lhs = "False";
        } else {
            lhs = "True";
        }
    } else {
        fputs("Expected == or != after ", stderr);
        fputs(lhs, stderr);
        fputs(" in logic\n", stderr);
        exit(1);
    }

    if (strcmp(tok->val, ")") == 0) {
        *val = lhs;
        return tok;
    } else if (strcmp(tok->val, "||") == 0) {
        /* OR */
        tok = logic(tok->next, &rhs);
        if (strcmp(lhs, "True") == 0 || strcmp(rhs, "True") == 0) {
            lhs = "True";
        } else {
            lhs = "False";
        }
    } else if (strcmp(tok->val, "&&") == 0) {
        /* AND */
        tok = logic(tok->next, &rhs);
        if (strcmp(lhs, "True") == 0 && strcmp(rhs, "True") == 0) {
            lhs = "True";
        } else {
            lhs = "False";
        }
    } else {
        fputs("Expected || or && in logic\n", stderr);
        exit(1);
    }

    *val = lhs;
    return tok;
}

Token *primary_logic(Token *tok, char **val) {
    /* Starting ( */
    if (strcmp(tok->val, "(") != 0) {
        fputs("Expected logic to begin with (\n", stderr);
        exit(1);
    }
    tok = tok->next;

    tok = logic(tok, val);

    if (strcmp(tok->val, ")") != 0) {
        fputs("Expected logic to end with )\n", stderr);
        exit(1);
    }

    return tok;
}

int eval_predicate(Token *tok) {
    char *result;
    tok = primary_logic(tok, &result);
    return strcmp(result, "True") == 0;
}

Token *define(Token *tok, Directive *directive) {
    /* define = name "=" (logic | constant) */
    char *name = tok->val;
    tok = tok->next;
    if (strcmp(tok->val, "=") != 0) {
        fputs("define of ", stderr);
        fputs(name, stderr);
        fputs(" has a missing equals\n", stderr);
        exit(1);
    }
    tok = tok->next;

    char *val = calloc(MAX_STRING, sizeof(char));
    if (strcmp(tok->val, "(") == 0) {
        /* It is a logic. */
        tok = primary_logic(tok, &val);
    } else {
        /* It is a constant. */
        strcpy(val, tok->val);
    }

    /* Check for predicate. */
    tok = tok->next;
    if (tok != NULL) {
        if (!eval_predicate(tok)) {
            /* Nothing more to do. */
            return tok;
        }
    }

    /* Update existing variable, or else, add to the end of variables. */
    /* Special case: empty variables. */
    if (variables == NULL) {
        variables = calloc(1, sizeof(Variable));
        variables->name = name;
        variables->val = val;
    }

    Variable *var;
    for (var = variables; var->next != NULL; var = var->next) {
        if (strcmp(var->next->name, name) == 0) {
            var->next->val = val;
            break;
        }
    }
    if (var->next == NULL) {
        /* We did not update an existing variable. */
        var->next = calloc(1, sizeof(Variable));
        var->next->name = name;
        var->next->val = val;
    }

    return tok;
}

int interpret(Directive *directive) {
    /* directive = (build | improve | define | jump | uninstall) predicate? */
    Token *tok = directive->tok;
    if (strcmp(tok->val, "build:") == 0) {
        tok = fill(tok->next, directive, TYPE_BUILD);
    } else if (strcmp(tok->val, "improve:") == 0) {
        tok = fill(tok->next, directive, TYPE_IMPROVE);
    } else if (strcmp(tok->val, "jump:") == 0) {
        tok = fill(tok->next, directive, TYPE_JUMP);
    } else if (strcmp(tok->val, "define:") == 0) {
        tok = define(tok->next, directive);
        return 1; /* There is no codegen for a define. */
    } else if (strcmp(tok->val, "uninstall:") == 0) {
        tok = fill(tok->next, directive, TYPE_UNINSTALL);
        while (tok != NULL) {
            if (strcmp(tok->val, "(") == 0) {
                break;
            }
            if (strlen(directive->arg) + strlen(tok->val) + 1 > MAX_STRING) {
                fputs("somehow you have managed to have too many uninstall arguments.\n", stderr);
                exit(1);
            }
            directive->arg = strcat(directive->arg, " ");
            directive->arg = strcat(directive->arg, tok->val);
            tok = tok->next;
        }
    }

    if (tok != NULL) {
        return !eval_predicate(tok);
    }
    return 0;
}

Directive *interpreter(Directive *directives) {
    Directive *directive;
    Directive *last = NULL;
    for (directive = directives; directive != NULL; directive = directive->next) {
        if (interpret(directive)) {
            /* This means this directive needs to be removed from the linked list. */
            if (last == NULL) {
                /* First directive. */
                directives = directive->next;
            } else {
                last->next = directive->next;
            }
        } else {
            last = directive;
        }
    }
    return directives;
}

/* Script generator. */
FILE *start_script(int id, int bash_build) {
    /* Create the file /steps/$id.sh */
    char *filename = calloc(MAX_STRING, sizeof(char));
    strcpy(filename, "/steps/");
    strcat(filename, int2str(id, 10, 0));
    strcat(filename, ".sh");

    FILE *out = fopen(filename, "w");
    if (out == NULL) {
        fputs("Error opening output file ", stderr);
        fputs(filename, stderr);
        fputs("\n", stderr);
        exit(1);
    }

    if (bash_build) {
        fputs("#!/bin/bash\n", out);
        if (strcmp(get_var("INTERACTIVE"), "True") == 0) {
            if (bash_build != 1) {
                fputs("set -E\ntrap 'env PS1=\"[TRAP] \\w # \" bash -i' ERR\n", out);
            } else {
                /* FIXME early bash has buggy ERR trap handling */
                fputs("set -e\ntrap 'bash -c '\"'\"'while true; do printf \""
                "[TRAP - use Ctrl+D] $(pwd) # \"; eval \"$(cat)\"; done'\"'\"'' EXIT\n",
                out);
            }
        } else {
            fputs("set -e\n", out);
        }
        fputs("cd /steps\n", out);
        fputs(". ./bootstrap.cfg\n", out);
        fputs(". ./env\n", out);
        fputs(". ./helpers.sh\n", out);
    } else {
        fputs("set -ex\n", out);
        fputs("cd /steps\n", out);
        output_config(out);
        FILE *env = fopen("/steps/env", "r");
        char *line = calloc(MAX_STRING, sizeof(char));
        while (fgets(line, MAX_STRING, env) != 0) {
            /* Weird M2-Planet behaviour. */
            if (*line == 0) {
                break;
            }
            fputs(line, out);
            line = calloc(MAX_STRING, sizeof(char));
        }
        fclose(env);
    }

    return out;
}

void output_call_script(FILE *out, char *type, char *name, int bash_build, int source) {
    if (bash_build) {
        if (source) {
            fputs(". ", out);
        } else {
            fputs("bash ", out);
        }
    } else {
        fputs("kaem --file ", out);
    }
    fputs("/steps/", out);
    if (strlen(type) != 0) {
        fputs(type, out);
        fputs("/", out);
    }
    fputs(name, out);
    fputs(".sh\n", out);
}

void output_build(FILE *out, Directive *directive, int pass_no, int bash_build) {
    if (bash_build) {
        fputs("build ", out);
        fputs(directive->arg, out);
        fputs(" pass", out);
        fputs(int2str(pass_no, 10, 0), out);
        fputs(".sh\n", out);
    } else {
        fputs("pkg=", out);
        fputs(directive->arg, out);
        fputs("\n", out);
        fputs("cd ${pkg}\n", out);
        fputs("kaem --file pass", out);
        fputs(int2str(pass_no, 10, 0), out);
        fputs(".kaem\n", out);
        fputs("cd ..\n", out);
    }
}

void generate_preseed_jump(int id) {
    FILE *out = fopen("/preseed-jump.kaem", "w");
    fputs("set -ex\n", out);
    fputs("PATH=/usr/bin\n", out);
    fputs("bash /steps/", out);
    fputs(int2str(id, 10, 0), out);
    fputs(".sh\n", out);
    fclose(out);
}

void generate(Directive *directives) {
    /*
     * We are separating the stages given in the mainfest into a bunch of
     * smaller scripts. The following conditions call for the creation of
     * a new script:
     * - a jump
     * - build of bash
     */

    int counter = 0;

    /* Initially, we use kaem, not bash. */
    int bash_build = 0;

    FILE *out = start_script(counter, bash_build);
    counter += 1;

    Directive *directive;
    Directive *past;
    char *filename;
    int pass_no;
    for (directive = directives; directive != NULL; directive = directive->next) {
        if (directive->type == TYPE_BUILD) {
            /* Get what pass number this is. */
            pass_no = 1;
            for (past = directives; past != directive; past = past->next) {
                if (strcmp(past->arg, directive->arg) == 0) {
                    pass_no += 1;
                }
            }
            output_build(out, directive, pass_no, bash_build);
            if (strncmp(directive->arg, "bash-", 5) == 0) {
                if (!bash_build) {
                    /*
                     * We are transitioning from bash to kaem, the point at which "early
                     * preseed" occurs. So generate the preseed jump script at this point.
                     */
                    generate_preseed_jump(counter);
                }
                bash_build += 1;
                /* Create call to new script. */
                output_call_script(out, "", int2str(counter, 10, 0), bash_build, 0);
                fclose(out);
                out = start_script(counter, bash_build);
                counter += 1;
            }
        } else if (directive->type == TYPE_IMPROVE) {
            output_call_script(out, "improve", directive->arg, bash_build, 1);
        } else if (directive->type == TYPE_JUMP) {
            /*
             * Create /init to call new script.
             * We actually do this by creating /init.X for some number X, and then
             * moving that to /init at the appropriate time.
             */
            filename = calloc(MAX_STRING, sizeof(char));
            if (bash_build) {
                fputs("mv /init /init.bak\n", out);
                /* Move new init to /init. */
                strcpy(filename, "/init.");
                strcat(filename, int2str(counter, 10, 0));
                fputs("cp ", out);
                fputs(filename, out);
                fputs(" /init\n", out);
                fputs("chmod 755 /init\n", out);
            } else {
                strcpy(filename, "/kaem.run.");
                strcat(filename, int2str(counter, 10, 0));
                fputs("cp ", out);
                fputs(filename, out);
                fputs(" /kaem.run\n", out);
                fputs("cp /usr/bin/kaem /init\n", out);
                fputs("chmod 755 /init\n", out);
            }

            output_call_script(out, "jump", directive->arg, bash_build, 1);
            fclose(out);

            if (bash_build) {
                out = fopen(filename, "w");
                if (out == NULL) {
                    fputs("Error opening /init\n", stderr);
                    exit(1);
                }
                fputs("#!/bin/bash\n", out);
            } else {
                out = fopen(filename, "w");
                if (out == NULL) {
                    fputs("Error opening /kaem.run\n", stderr);
                    exit(1);
                }
                fputs("set -ex\n", out);
            }
            output_call_script(out, "", int2str(counter, 10, 0), bash_build, 0);
            fclose(out);
            out = start_script(counter, bash_build);
            counter += 1;
        } else if (directive->type == TYPE_UNINSTALL) {
            fputs("uninstall ", out);
            fputs(directive->arg, out);
            fputs("\n", out);
        }
    }
    fclose(out);
}

void main(int argc, char **argv) {
    if (argc != 2) {
        fputs("Usage: script-generator <script>\n", stderr);
        exit(1);
    }

    FILE *in = fopen(argv[1], "r");
    if (in == NULL) {
        fputs("Error opening input file\n", stderr);
        exit(1);
    }
    Directive *directives = tokenizer(in);
    fclose(in);
    load_config();
    directives = interpreter(directives);
    generate(directives);
    FILE *config = fopen("/steps/bootstrap.cfg", "w");
    output_config(config);
    fclose(config);
}

File /tmp/M2-Mesoplanet-000000

(Source not found at '(null)')

File /tmp/M2-Planet-000000

(Source not found at '(null)')

File /tmp/blood-elf-000000

(Source not found at '(null)')

File /tmp/M1-macro-000000

(Source not found at '(null)')

File /script-generator.x86.checksums

Live-bootstrap source file is 'seed/script-generator.x86.checksums'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/seed/script-generator.x86.checksums
dc6106dbc02839cdc9e3e2348432242eb6d33d840ab74badfd63c3c9997462b9  script-generator

File /steps/manifest

Live-bootstrap source file is 'steps/manifest'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/manifest
# SPDX-FileCopyrightText: 2023 fosslinux <fosslinux@aussies.space>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file follows a very simple, human readable and machine parseable syntax.
# Each line is in the format:
# <directive>: <arguments> <predicate>
#
# The supported directives and arguments are:
# - build; simply builds a particular package.
#   eg, build: make-3.82
# - improve; runs a script that makes a distinct and logical improvement to the system.
#   eg, improve: use_fhs
# - define: define a variable based upon other variables
#   eg, define: BUILD_FIWIX = KERNEL_BOOTSTRAP == True || BUILD_KERNELS == True
# - jump: jump (usually) to a new kernel, executes a script with that name
#   eg, jump: fiwix
# - uninstall; removes a package or filenames
#   eg, uninstall: perl-5.6.2
#   eg, uninstall: /usr/bin/lex /usr/bin/flex
#
# The following directives have special significance:
# - build directives beginning with "bash" (as well as jumps) trigger the generation of
#   a new script
# - the first improve directive containing "network" is used by generator.py to deduce
#   what source files need to be downloaded in advance (files referenced after that will
#   be downloaded during bootstrap, unless --external-sources is given)
#
# Other features:
# - predicate; based on variables set in bootstrap.cfg, require for something to execute
#   must be enclosed in brackets with spaces padded
#   eg, build: fiwix-1.5.0-lb1 ( BUILD_FIWIX == True )

build: checksum-transcriber-1.0
build: simple-patch-1.0
build: mes-0.26
build: tcc-0.9.26
build: tcc-0.9.27
define: BUILD_FIWIX = ( KERNEL_BOOTSTRAP == True || BUILD_KERNELS == True )
build: fiwix-1.5.0-lb1 ( BUILD_FIWIX == True )
build: lwext4-1.0.0-lb1 ( BUILD_FIWIX == True )
build: kexec-fiwix-1.0 ( BUILD_FIWIX == True )
jump: fiwix ( KERNEL_BOOTSTRAP == True )
define: JOBS = 1 ( KERNEL_BOOTSTRAP == True )
build: make-3.82
build: patch-2.5.9
build: gzip-1.2.4
build: tar-1.12
build: sed-4.0.9
build: bzip2-1.0.8
build: coreutils-5.0
build: byacc-20240109
build: bash-2.05b
improve: setup_repo
improve: update_env
improve: merged_usr
improve: populate_device_nodes
define: CONSOLES = ( INTERACTIVE == True && CHROOT == False )
improve: open_console ( CONSOLES == True )
build: tcc-0.9.27
improve: musl_libdir
build: musl-1.1.24
build: tcc-0.9.27
build: musl-1.1.24
build: tcc-0.9.27
build: sed-4.0.9
build: bzip2-1.0.8
build: m4-1.4.7
build: heirloom-devtools-070527
build: flex-2.5.11
build: flex-2.6.4
uninstall: heirloom-devtools-070527
build: bison-3.4.1
build: bison-3.4.1
build: bison-3.4.1
build: grep-2.4
build: diffutils-2.7
build: coreutils-5.0
build: coreutils-6.10
build: gawk-3.0.4
build: perl-5.000
build: perl-5.003
build: perl5.004-05
build: perl5.005-03
build: perl-5.6.2
uninstall: perl-5.000 perl-5.003 perl5.004-05 perl5.005-03
build: autoconf-2.52
build: automake-1.6.3
build: automake-1.6.3
build: autoconf-2.53
build: automake-1.7
build: autoconf-2.54
build: autoconf-2.55
build: automake-1.7.8
build: autoconf-2.57
build: autoconf-2.59
build: automake-1.8.5
build: help2man-1.36.4
build: autoconf-2.61
build: automake-1.9.6
build: automake-1.10.3
build: autoconf-2.64
build: automake-1.11.2
build: autoconf-2.69
build: libtool-2.2.4
build: automake-1.15.1
build: binutils-2.30
build: musl-1.1.24
build: tcc-0.9.27
improve: populate_device_nodes
build: gcc-4.0.4
build: findutils-4.2.33
build: musl-1.2.4
build: linux-headers-4.14.341-openela
build: gcc-4.0.4
build: util-linux-2.19.1
build: e2fsprogs-1.45.7
build: dhcpcd-10.0.1
build: kbd-1.15
build: make-3.82
build: ed-1.4
build: bc-1.07.1
define: BUILD_LINUX = ( CHROOT == False || BUILD_KERNELS == True )
build: kexec-linux-1.0.0 ( BUILD_LINUX == True )
build: kexec-tools-2.0.22 ( BUILD_LINUX == True )
improve: clean_sources
improve: clean_artifacts
build: linux-4.14.341-openela ( BUILD_LINUX == True )
jump: break ( INTERNAL_CI == pass1 )
improve: populate_device_nodes
jump: linux ( CHROOT == False )
jump: move_disk ( KERNEL_BOOTSTRAP == True )
improve: finalize_job_count
improve: finalize_fhs
improve: open_console ( CONSOLES == True )
improve: swap ( SWAP_SIZE != 0 )
build: musl-1.2.4
build: curl-8.5.0
improve: get_network ( CHROOT == False )
build: bash-5.2.15
improve: open_console ( CONSOLES == True )
build: xz-5.4.1
build: file-5.44
build: libtool-2.4.7
build: tar-1.34
build: coreutils-9.4
build: pkg-config-0.29.2
build: make-4.2.1
build: gmp-6.2.1
build: autoconf-archive-2021.02.19
build: mpfr-4.1.0
build: mpc-1.2.1
build: flex-2.5.33
build: bison-2.3
build: bison-3.4.2
build: perl-5.10.1
build: dist-3.5-236
build: perl-5.32.1
uninstall: perl-5.6.2 perl-5.10.1
build: libarchive-3.5.2
build: openssl-3.0.13
build: ca-certificates-3.95
build: curl-8.5.0
build: zlib-1.2.13
build: automake-1.16.3
build: autoconf-2.71
build: patch-2.7.6
build: gettext-0.21
build: texinfo-6.7
build: gcc-4.7.4
build: binutils-2.41
build: gperf-3.1
build: libunistring-0.9.10
build: libffi-3.3
build: libatomic_ops-7.6.10
build: gc-8.0.4
build: guile-3.0.9
build: which-2.21
build: grep-3.7
build: sed-4.8
build: autogen-5.18.16
build: musl-1.2.4
build: python-2.0.1
build: python-2.0.1
build: python-2.3.7
build: python-2.3.7
build: python-2.5.6
build: python-3.1.5
build: python-3.1.5
build: python-3.3.7
build: python-3.4.10
build: python-3.8.16
build: python-3.11.1
uninstall: python-2.0.1 python-2.3.7 python-3.1.5 python-3.3.7 python-3.4.10 python-3.8.16
jump: break ( INTERNAL_CI == pass1 ) # scripts are generated in pass1
build: gcc-10.4.0
build: binutils-2.41
build: gcc-13.1.0
build: grub-2.06 ( BUILD_LINUX == True )
improve: make_bootable ( CHROOT == False )
build: libmd-1.1.0
build: libbsd-0.11.8
build: shadow-4.14.3
build: opendoas-6.8.2
build: gzip-1.13
build: diffutils-3.10
build: gawk-5.3.0
build: m4-1.4.19
improve: cleanup_filesystem
improve: null_time ( FORCE_TIMESTAMPS == True )
improve: update_checksums ( UPDATE_CHECKSUMS == True )
improve: after

File /steps/checksum-transcriber-1.0/pass1.kaem

Live-bootstrap source file is 'steps/checksum-transcriber-1.0/pass1.kaem'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/checksum-transcriber-1.0/pass1.kaem
#!/bin/sh

# SPDX-FileCopyrightText: 2023 fosslinux <fosslinux@aussies.space>
#
# SPDX-License-Identifier: GPL-3.0-or-later

set -ex

# Build & install
M2-Mesoplanet --architecture ${ARCH} -f src/checksum-transcriber.c -o ${BINDIR}/checksum-transcriber

# Checksums
if match x${UPDATE_CHECKSUMS} xTrue; then
    sha256sum -o ${pkg}.${ARCH}.checksums \
        /usr/bin/checksum-transcriber

    cp ${pkg}.${ARCH}.checksums ${SRCDIR}
else
    sha256sum -c ${pkg}.${ARCH}.checksums
fi

File /steps/checksum-transcriber-1.0/src/checksum-transcriber.c

Live-bootstrap source file is 'steps/checksum-transcriber-1.0/src/checksum-transcriber.c'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/checksum-transcriber-1.0/src/checksum-transcriber.c
/*
 * SPDX-FileCopyrightText: 2022 fosslinux <fosslinux@aussies.space>
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <bootstrappable.h>
#include <unistd.h>

#define MAX_STRING 4096
#define MAX_TOKENS 3

char *get_distfiles(char **envp) {
    char *envvar = "DISTFILES=";
    int i = 0;
    while (envp[i] != NULL && strncmp(envp[i], envvar, strlen(envvar)) != 0) i += 1;
    // Now we have distfiles= - get just the part we want.
    require(envp[i] != NULL, "Unable to find distfiles environment variable");
    return envp[i] + strlen(envvar);
}

int main(int argc, char **argv, char **envp) {
    // Random file things
    require(argc == 2, "Usage: checksum-transcriber FILENAME");
    char *input = argv[1];
    FILE *in = fopen(input, "r");
    require(in != NULL, "File does not exist");
    char *output = calloc(MAX_STRING, sizeof(char));
    require(strcpy(output, input) != NULL, "Failed copying string");
    require(strcat(output, ".SHA256SUM") != NULL, "Failed concating string");
    FILE *out = fopen(output, "w+");
    require(out != NULL, "Failed opening output file");

    char *orig_line;
    char *line = calloc(MAX_STRING, sizeof(char));
    require(line != NULL, "Failed allocating string");
    char **tokens;
    char *new_line;
    char *checksum;
    char *filename;
    int i;
    fgets(line, MAX_STRING, in);
    while (strlen(line) != 0) {
        // Split each line into tokens
        orig_line = line;
        tokens = calloc(MAX_TOKENS, sizeof(char*));
        i = 0;
        while (i < MAX_TOKENS) {
            tokens[i] = line;
            new_line = strchr(line, ' ');
            // Occurs when there are only two tokens
            if (new_line == NULL) break;
            line = new_line;
            line[0] = '\0';
            line += 1;
            i += 1;
        }
        line = strchr(line, '\n');
        line[0] = '\0';
        // Get checksum and filename
        checksum = tokens[1];
        if (tokens[2] != NULL) {
            filename = tokens[2];
        } else {
            filename = strrchr(tokens[0], '/');
            filename += 1;
        }
        // Put it all together
        fputs(checksum, out);
        fputs("  ", out);
        fputs(get_distfiles(envp), out);
        fputc('/', out);
        fputs(filename, out);
        fputc('\n', out);
        // Cleanup
        i = 0;
        free(orig_line);
        free(tokens);
        line = calloc(MAX_STRING, sizeof(char));
        require(line != NULL, "Failed allocating string");
        fgets(line, MAX_STRING, in);
    }

    // Clean up
    fclose(in);
    fclose(out);
}

File /steps/checksum-transcriber-1.0/checksum-transcriber-1.0.x86.checksums

Live-bootstrap source file is 'steps/checksum-transcriber-1.0/checksum-transcriber-1.0.x86.checksums'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/checksum-transcriber-1.0/checksum-transcriber-1.0.x86.checksums
19fd50d727e8a7feba8b6e369e68287d1922d8f623a156fb113d994891e96998  /usr/bin/checksum-transcriber

File /steps/simple-patch-1.0/pass1.kaem

Live-bootstrap source file is 'steps/simple-patch-1.0/pass1.kaem'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/simple-patch-1.0/pass1.kaem
#!/bin/sh

# SPDX-FileCopyrightText: 2023 fosslinux <fosslinux@aussies.space>
#
# SPDX-License-Identifier: GPL-3.0-or-later

set -ex

# Build & install
M2-Mesoplanet --architecture ${ARCH} -f src/simple-patch.c -o ${BINDIR}/simple-patch

# Checksums
if match x${UPDATE_CHECKSUMS} xTrue; then
    sha256sum -o ${pkg}.${ARCH}.checksums \
        /usr/bin/simple-patch

    cp ${pkg}.${ARCH}.checksums ${SRCDIR}
else
    sha256sum -c ${pkg}.${ARCH}.checksums
fi

File /steps/simple-patch-1.0/src/simple-patch.c

Live-bootstrap source file is 'steps/simple-patch-1.0/src/simple-patch.c'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/simple-patch-1.0/src/simple-patch.c
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "M2libc/bootstrappable.h"

/*
SPDX-FileCopyrightText: 2023 Richard Masters <grick23@gmail.com>
SPDX-License-Identifier: MIT

Simple Patch program.

This program is written in a subset of C called M2, which is from the
stage0-posix bootstrap project.

Example usage:
./simple-patch file_to_patch before_pattern_file after_pattern_file

*/

// function prototypes
void read_file_or_die(char *file_name, char **buffer, int *file_size);
void patch_buffer_or_die(char *patch_file_before_buffer, int patch_file_before_size,
                 char *before_pattern_buffer, int before_pattern_size,
                 char *after_pattern_buffer, int after_pattern_size,
                 char *patch_file_after_buffer);
void writestr_fd(int fd, char *str);
int memsame(char *search_buffer, int search_size,
            char *pattern_buffer, int pattern_size);


int main(int argc, char **argv) {
    char *patch_file_before_buffer;
    int patch_file_before_size;

    char *before_pattern_buffer;
    int before_pattern_size;

    char *after_pattern_buffer;
    int after_pattern_size;

    int patch_file_after_size;
    char *patch_file_after_buffer;

    int patch_file_fd;

    read_file_or_die(argv[1], &patch_file_before_buffer, &patch_file_before_size);
    read_file_or_die(argv[2], &before_pattern_buffer, &before_pattern_size);
    read_file_or_die(argv[3], &after_pattern_buffer, &after_pattern_size);

    patch_file_after_size = patch_file_before_size - before_pattern_size + after_pattern_size;
    patch_file_after_buffer = calloc(patch_file_after_size, sizeof(char));

    patch_buffer_or_die(patch_file_before_buffer, patch_file_before_size,
                 before_pattern_buffer, before_pattern_size,
                 after_pattern_buffer, after_pattern_size,
                 patch_file_after_buffer);
    
    patch_file_fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0);
    write(patch_file_fd, patch_file_after_buffer, patch_file_after_size);
    close(patch_file_fd);

    return EXIT_SUCCESS;
}


void read_file_or_die(char *file_name, char **buffer, int *file_size) {
    int file_fd;
    int num_bytes_read;

    file_fd = open(file_name, O_RDONLY, 0);
    if (file_fd == -1) {
        writestr_fd(2, "Could not open file: ");
        writestr_fd(2, file_name);
        writestr_fd(2, "\n");
        exit(1);
    }
    // determine file size
    *file_size = lseek(file_fd, 0, SEEK_END);
    // go back to beginning of file
    lseek(file_fd, 0, SEEK_SET);
    // alloc a buffer to read the entire file
    *buffer = calloc(*file_size, sizeof(char));

    // read the entire patch file
    num_bytes_read = read(file_fd, *buffer, *file_size);
    if (num_bytes_read != *file_size) {
        writestr_fd(2, "Could not read file: ");
        writestr_fd(2, file_name);
        writestr_fd(2, "\n");
        exit(1);
    }
    close(file_fd);
}

void patch_buffer_or_die(char *patch_file_before_buffer, int patch_file_before_size,
                 char *before_pattern_buffer, int before_pattern_size,
                 char *after_pattern_buffer, int after_pattern_size,
                 char *patch_file_after_buffer) {

    char *pos = patch_file_before_buffer;
    int prefix_len = 0;

    // look for the pattern at every offset
    while (prefix_len < patch_file_before_size) {
        // if we find the pattern, replace it and return
        if (memsame(pos, patch_file_before_size - prefix_len, before_pattern_buffer, before_pattern_size)) {
           memcpy(patch_file_after_buffer, patch_file_before_buffer, prefix_len);
           memcpy(patch_file_after_buffer + prefix_len, after_pattern_buffer, after_pattern_size);
           memcpy(patch_file_after_buffer + prefix_len + after_pattern_size, 
                  patch_file_before_buffer + prefix_len + before_pattern_size, 
                  patch_file_before_size - (prefix_len + before_pattern_size));
           return;
        }
        pos = pos + 1;
        prefix_len = prefix_len + 1;
    }

    /* if we don't find the pattern, something is wrong, so exit with error */
    exit(1);
}

/*
    Write the string to the given file descriptor.
*/
void writestr_fd(int fd, char *str) {
    write(fd, str, strlen(str));
}

/*
    Is the pattern located at the start of the search buffer
    (and not exceeding the length of the search buffer)?
*/

int memsame(char *search_buffer, int search_size,
            char *pattern_buffer, int pattern_size) {
    int check_offset = 0;

    if (pattern_size > search_size) {
        return FALSE;
    }
    while (check_offset < pattern_size) {
        if (search_buffer[check_offset] != pattern_buffer[check_offset]) {
             return FALSE;
        }
        check_offset = check_offset + 1;
    }
    return TRUE;
}

File /steps/simple-patch-1.0/simple-patch-1.0.x86.checksums

Live-bootstrap source file is 'steps/simple-patch-1.0/simple-patch-1.0.x86.checksums'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/simple-patch-1.0/simple-patch-1.0.x86.checksums
f66b8200c9237a7d5fe6a3d4d94f1ae661009d8ad14efdc7e95ac4cb8c4775e8  /usr/bin/simple-patch

File /steps/mes-0.26/pass1.kaem

Live-bootstrap source file is 'steps/mes-0.26/pass1.kaem'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/mes-0.26/pass1.kaem
#!/bin/sh

# SPDX-FileCopyrightText: 2020-2023 Andrius Å tikonas <andrius@stikonas.eu>
# SPDX-FileCopyrightText: 2020-2022 fosslinux <fosslinux@aussies.space>
#
# SPDX-License-Identifier: GPL-3.0-or-later

set -ex

# Variables
MES_ARENA=20000000
MES_MAX_ARENA=20000000
MES_STACK=6000000
MES=${BINDIR}/mes-m2
LIBDIR=${MES_PREFIX}/lib

if match ${ARCH} x86; then
    MES_ARCH=x86
    ARCH_BITS=32
fi
if match ${ARCH} amd64; then
    MES_ARCH=x86_64
    ARCH_BITS=64
fi
if match ${ARCH} riscv64; then
    MES_ARCH=riscv64
    ARCH_BITS=64
fi

# Check tarball checksums
checksum-transcriber sources
sha256sum -c sources.SHA256SUM

# Unpack
mkdir build
cd build
ungz --file ${DISTFILES}/${NYACC_PKG}-lb1.tar.gz --output ${NYACC_PKG}.tar
ungz --file ${DISTFILES}/${MES_PKG}.tar.gz --output ${MES_PKG}.tar
untar --file ${NYACC_PKG}.tar
untar --non-strict --file ${MES_PKG}.tar # ignore symlinks
mes_run=${MES_PREFIX}/kaem.run
replace --file ${mes_run} --output ${mes_run} --match-on 0x1000000 --replace-with 0x8048000

rm ${NYACC_PKG}.tar ${MES_PKG}.tar

cp ../files/config.h ${MES_PREFIX}/include/mes

cd ${MES_PREFIX}
mkdir include/arch
cp include/linux/${MES_ARCH}/syscall.h include/arch/syscall.h
cp include/linux/${MES_ARCH}/kernel-stat.h include/arch/kernel-stat.h

# Remove pregenerated files
rm mes/module/mes/psyntax.pp mes/module/mes/psyntax.pp.header

# These files are symlinked in the repo
cp mes/module/srfi/srfi-9-struct.mes mes/module/srfi/srfi-9.mes
cp mes/module/srfi/srfi-9/gnu-struct.mes mes/module/srfi/srfi-9/gnu.mes

# Build mes-m2
kaem --verbose --strict --file kaem.${MES_ARCH}
cp bin/mes-m2 ${BINDIR}/mes-m2
chmod 755 ${BINDIR}/mes-m2

# Rebuild pregenerated files in nyacc
cd ../${NYACC_PKG}
rm module/nyacc/lang/c99/mach.d/c99-act.scm module/nyacc/lang/c99/mach.d/c99-tab.scm \
    module/nyacc/lang/c99/mach.d/c99cx-act.scm module/nyacc/lang/c99/mach.d/c99cx-tab.scm \
    module/nyacc/lang/c99/mach.d/c99x-act.scm module/nyacc/lang/c99/mach.d/c99x-tab.scm \
    module/nyacc/lang/c99/mach.d/cpp-act.scm module/nyacc/lang/c99/mach.d/cpp-tab.scm
${MES} -L module gen-cpp-files.scm
# More RAM for this one!
MES_ARENA=32000000
${MES} -L module gen-c99-files.scm
MES_ARENA=20000000
${MES} -L module gen-c99cx-files.scm
cd ../${MES_PKG}

# Create mescc.scm
mescc_in=scripts/mescc.scm.in
replace --file ${mescc_in} --output ${mescc_in} --match-on @prefix@ --replace-with ${PREFIX}
replace --file ${mescc_in} --output ${mescc_in} --match-on @VERSION@ --replace-with ${MES_VERSION}
replace --file ${mescc_in} --output ${mescc_in} --match-on @mes_cpu@ --replace-with ${MES_ARCH}
replace --file ${mescc_in} --output ${mescc_in} --match-on @mes_kernel@ --replace-with linux

# Patch out double compilation bug
replace --file ${mescc_in} --output ${mescc_in} --match-on "(mescc:main (command-line))" --replace-with ");"

mescc_scm=${BINDIR}/mescc.scm
cp ${mescc_in} ${mescc_scm}
chmod 755 ${mescc_scm}


# Recompile Mes and Mes C library using mes-m2 bootstrapped Mes

alias mescc="${MES} -e main ${BINDIR}/mescc.scm -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -c"

# Start with crt1.o
mescc lib/linux/${MES_ARCH}-mes-mescc/crt1.c -o lib/${MES_ARCH}-mes/crt1.o

# Now for libc-mini.a
mescc lib/mes/__init_io.c
mescc lib/mes/eputs.c
mescc lib/mes/oputs.c
mescc lib/mes/globals.c
mescc lib/stdlib/exit.c
mescc lib/linux/${MES_ARCH}-mes-mescc/_exit.c
mescc lib/linux/${MES_ARCH}-mes-mescc/_write.c
mescc lib/stdlib/puts.c
mescc lib/string/strlen.c
catm ${LIBDIR}/${MES_ARCH}-mes/libc-mini.a __init_io.o eputs.o oputs.o globals.o exit.o _exit.o _write.o puts.o strlen.o
catm ${LIBDIR}/${MES_ARCH}-mes/libc-mini.s __init_io.s eputs.s oputs.s globals.s exit.s _exit.s _write.s puts.s strlen.s

# libmescc.a
mescc lib/linux/${MES_ARCH}-mes-mescc/syscall-internal.c
catm ${LIBDIR}/${MES_ARCH}-mes/libmescc.a globals.o syscall-internal.o
catm ${LIBDIR}/${MES_ARCH}-mes/libmescc.s globals.s syscall-internal.s

# libc.a
mescc lib/ctype/isnumber.c
mescc lib/mes/abtol.c
mescc lib/mes/cast.c
mescc lib/mes/eputc.c
mescc lib/mes/fdgetc.c
mescc lib/mes/fdputc.c
mescc lib/mes/fdputs.c
mescc lib/mes/fdungetc.c
mescc lib/mes/itoa.c
mescc lib/mes/ltoa.c
mescc lib/mes/ltoab.c
mescc lib/mes/mes_open.c
mescc lib/mes/ntoab.c
mescc lib/mes/oputc.c
mescc lib/mes/ultoa.c
mescc lib/mes/utoa.c
mescc lib/ctype/isdigit.c
mescc lib/ctype/isspace.c
mescc lib/ctype/isxdigit.c
mescc lib/mes/assert_msg.c
mescc lib/posix/write.c
mescc lib/stdlib/atoi.c
mescc lib/linux/lseek.c
mescc lib/mes/__assert_fail.c
mescc lib/mes/__buffered_read.c
mescc lib/mes/__mes_debug.c
mescc lib/posix/execv.c
mescc lib/posix/getcwd.c
mescc lib/posix/getenv.c
mescc lib/posix/isatty.c
mescc lib/posix/open.c
mescc lib/posix/buffered-read.c
mescc lib/posix/setenv.c
mescc lib/posix/wait.c
mescc lib/dirent/closedir.c
mescc lib/dirent/opendir.c
mescc lib/stdio/fgetc.c
mescc lib/stdio/fputc.c
mescc lib/stdio/fputs.c
mescc lib/stdio/getc.c
mescc lib/stdio/getchar.c
mescc lib/stdio/putc.c
mescc lib/stdio/putchar.c
mescc lib/stdio/ungetc.c
mescc lib/stdlib/calloc.c
mescc lib/stdlib/free.c
mescc lib/stdlib/realloc.c
mescc lib/string/memchr.c
mescc lib/string/memcmp.c
mescc lib/string/memcpy.c
mescc lib/string/memmove.c
mescc lib/string/memset.c
mescc lib/string/strcmp.c
mescc lib/string/strcpy.c
mescc lib/string/strncmp.c
mescc lib/posix/raise.c
mescc lib/linux/access.c
mescc lib/linux/brk.c
mescc lib/linux/chdir.c
mescc lib/linux/chmod.c
mescc lib/linux/clock_gettime.c
mescc lib/linux/dup.c
mescc lib/linux/dup2.c
mescc lib/linux/execve.c
mescc lib/linux/fork.c
mescc lib/linux/fsync.c
mescc lib/linux/_getcwd.c
mescc lib/linux/gettimeofday.c
mescc lib/linux/ioctl3.c
mescc lib/linux/malloc.c
mescc lib/linux/_open3.c
mescc lib/linux/_read.c
mescc lib/linux/readdir.c
mescc lib/linux/rename.c
mescc lib/linux/time.c
mescc lib/linux/umask.c
mescc lib/linux/uname.c
mescc lib/linux/unlink.c
mescc lib/linux/utimensat.c
mescc lib/linux/wait4.c
mescc lib/linux/waitpid.c
mescc lib/linux/${MES_ARCH}-mes-mescc/syscall.c
mescc lib/linux/getpid.c
mescc lib/linux/kill.c
mescc lib/linux/pipe.c
mescc lib/linux/stat.c
mescc lib/linux/lstat.c
mescc lib/linux/mkdir.c
mescc lib/linux/rmdir.c
mescc lib/linux/link.c
mescc lib/linux/symlink.c
mescc lib/linux/close.c
mescc lib/linux/nanosleep.c
mescc lib/linux/fcntl.c
mescc lib/linux/fstat.c
mescc lib/linux/getdents.c
catm ${LIBDIR}/${MES_ARCH}-mes/libc.a __init_io.o eputs.o oputs.o globals.o exit.o _exit.o _write.o puts.o strlen.o isnumber.o abtol.o cast.o eputc.o fdgetc.o fdputc.o fdputs.o fdungetc.o itoa.o ltoa.o ltoab.o mes_open.o ntoab.o oputc.o ultoa.o utoa.o isdigit.o isspace.o isxdigit.o assert_msg.o write.o atoi.o lseek.o __assert_fail.o __buffered_read.o __mes_debug.o execv.o getcwd.o getenv.o isatty.o open.o buffered-read.o setenv.o wait.o closedir.o opendir.o fgetc.o fputc.o fputs.o getc.o getchar.o putc.o putchar.o ungetc.o calloc.o free.o malloc.o realloc.o memchr.o memcmp.o memcpy.o memmove.o memset.o strcmp.o strcpy.o strncmp.o raise.o access.o brk.o chdir.o chmod.o clock_gettime.o dup.o dup2.o execve.o fork.o fsync.o _getcwd.o gettimeofday.o ioctl3.o _open3.o _read.o readdir.o rename.o time.o umask.o uname.o unlink.o utimensat.o wait4.o waitpid.o syscall.o getpid.o kill.o pipe.o stat.o lstat.o mkdir.o rmdir.o link.o symlink.o close.o nanosleep.o fcntl.o fstat.o getdents.o
catm ${LIBDIR}/${MES_ARCH}-mes/libc.s __init_io.s eputs.s oputs.s globals.s exit.s _exit.s _write.s puts.s strlen.s isnumber.s abtol.s cast.s eputc.s fdgetc.s fdputc.s fdputs.s fdungetc.s itoa.s ltoa.s ltoab.s mes_open.s ntoab.s oputc.s ultoa.s utoa.s isdigit.s isspace.s isxdigit.s assert_msg.s write.s atoi.s lseek.s __assert_fail.s __buffered_read.s __mes_debug.s execv.s getcwd.s getenv.s isatty.s open.s buffered-read.s setenv.s wait.s closedir.s opendir.s fgetc.s fputc.s fputs.s getc.s getchar.s putc.s putchar.s ungetc.s calloc.s free.s malloc.s realloc.s memchr.s memcmp.s memcpy.s memmove.s memset.s strcmp.s strcpy.s strncmp.s raise.s access.s brk.s chdir.s chmod.s clock_gettime.s dup.s dup2.s execve.s fork.s fsync.s _getcwd.s gettimeofday.s ioctl3.s _open3.s _read.s readdir.s rename.s time.s umask.s uname.s unlink.s utimensat.s wait4.s waitpid.s syscall.s getpid.s kill.s pipe.s stat.s lstat.s mkdir.s rmdir.s link.s symlink.s close.s nanosleep.s fcntl.s fstat.s getdents.s

# Build mes itself
mescc src/builtins.c
mescc src/cc.c
mescc src/core.c
mescc src/display.c
mescc src/eval-apply.c
mescc src/gc.c
mescc src/globals.c
mescc src/hash.c
mescc src/lib.c
mescc src/math.c
mescc src/mes.c
mescc src/module.c
mescc src/posix.c
mescc src/reader.c
mescc src/stack.c
mescc src/string.c
mescc src/struct.c
mescc src/symbol.c
mescc src/variable.c
mescc src/vector.c

# Link everything into new mes executable
${MES} -e main ${mescc_scm} -- --base-address 0x08048000 -L ${LIBDIR} -o ${BINDIR}/mes builtins.o cc.o core.o display.o eval-apply.o gc.o globals.o hash.o lib.o math.o mes.o module.o posix.o reader.o stack.o string.o struct.o symbol.o variable.o vector.o -lc -lmescc

# libc+tcc.a
alias mescc="${BINDIR}/mes -e main ${BINDIR}/mescc.scm -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -c"
mescc lib/ctype/islower.c
mescc lib/ctype/isupper.c
mescc lib/ctype/tolower.c
mescc lib/ctype/toupper.c
mescc lib/mes/abtod.c
mescc lib/mes/dtoab.c
mescc lib/mes/search-path.c
mescc lib/posix/execvp.c
mescc lib/stdio/fclose.c
mescc lib/stdio/fdopen.c
mescc lib/stdio/ferror.c
mescc lib/stdio/fflush.c
mescc lib/stdio/fopen.c
mescc lib/stdio/fprintf.c
mescc lib/stdio/fread.c
mescc lib/stdio/fseek.c
mescc lib/stdio/ftell.c
mescc lib/stdio/fwrite.c
mescc lib/stdio/printf.c
mescc lib/stdio/remove.c
mescc lib/stdio/snprintf.c
mescc lib/stdio/sprintf.c
mescc lib/stdio/sscanf.c
mescc lib/stdio/vfprintf.c
mescc lib/stdio/vprintf.c
mescc lib/stdio/vsnprintf.c
mescc lib/stdio/vsprintf.c
mescc lib/stdio/vsscanf.c
mescc lib/stdlib/abort.c
mescc lib/stdlib/qsort.c
mescc lib/stdlib/strtod.c
mescc lib/stdlib/strtof.c
mescc lib/stdlib/strtol.c
mescc lib/stdlib/strtold.c
mescc lib/stdlib/strtoll.c
mescc lib/stdlib/strtoul.c
mescc lib/stdlib/strtoull.c
mescc lib/string/memmem.c
mescc lib/string/strcat.c
mescc lib/string/strchr.c
mescc lib/string/strlwr.c
mescc lib/string/strncpy.c
mescc lib/string/strrchr.c
mescc lib/string/strstr.c
mescc lib/string/strupr.c
mescc lib/stub/sigaction.c
mescc lib/stub/ldexp.c
mescc lib/stub/mprotect.c
mescc lib/stub/localtime.c
mescc lib/stub/sigemptyset.c
mescc lib/${MES_ARCH}-mes-mescc/setjmp.c
mescc lib/linux/close.c
mescc lib/linux/rmdir.c
mescc lib/linux/stat.c
catm ${LIBDIR}/${MES_ARCH}-mes/libc+tcc.a ${LIBDIR}/${MES_ARCH}-mes/libc.a islower.o isupper.o tolower.o toupper.o abtod.o dtoab.o search-path.o execvp.o fclose.o fdopen.o ferror.o fflush.o fopen.o fprintf.o fread.o fseek.o ftell.o fwrite.o printf.o remove.o snprintf.o sprintf.o sscanf.o vfprintf.o vprintf.o vsnprintf.o vsprintf.o vsscanf.o abort.o qsort.o strtod.o strtof.o strtol.o strtold.o strtoll.o strtoul.o strtoull.o memmem.o strcat.o strchr.o strlwr.o strncpy.o strrchr.o strstr.o strupr.o sigaction.o ldexp.o mprotect.o localtime.o sigemptyset.o setjmp.o close.o rmdir.o stat.o
catm ${LIBDIR}/${MES_ARCH}-mes/libc+tcc.s ${LIBDIR}/${MES_ARCH}-mes/libc.s islower.s isupper.s tolower.s toupper.s abtod.s dtoab.s search-path.s execvp.s fclose.s fdopen.s ferror.s fflush.s fopen.s fprintf.s fread.s fseek.s ftell.s fwrite.s printf.s remove.s snprintf.s sprintf.s sscanf.s vfprintf.s vprintf.s vsnprintf.s vsprintf.s vsscanf.s abort.s qsort.s strtod.s strtof.s strtol.s strtold.s strtoll.s strtoul.s strtoull.s memmem.s strcat.s strchr.s strlwr.s strncpy.s strrchr.s strstr.s strupr.s sigaction.s ldexp.s mprotect.s localtime.s sigemptyset.s setjmp.s close.s rmdir.s stat.s

# Make directories
mkdir ${PREFIX}/lib/linux ${INCDIR}/mes ${INCDIR}/sys ${INCDIR}/linux ${INCDIR}/arch
mkdir ${PREFIX}/lib/${MES_ARCH}-mes ${PREFIX}/lib/linux/${MES_ARCH}-mes ${INCDIR}/linux/${MES_ARCH}

# Install libraries
cp ${LIBDIR}/${MES_ARCH}-mes/libc.a ${PREFIX}/lib/${MES_ARCH}-mes/
cp ${LIBDIR}/${MES_ARCH}-mes/libc+tcc.a ${PREFIX}/lib/${MES_ARCH}-mes/
cp ${LIBDIR}/${MES_ARCH}-mes/libmescc.a ${PREFIX}/lib/${MES_ARCH}-mes/
cp ${LIBDIR}/${MES_ARCH}-mes/libc.s ${PREFIX}/lib/${MES_ARCH}-mes/
cp ${LIBDIR}/${MES_ARCH}-mes/libc+tcc.s ${PREFIX}/lib/${MES_ARCH}-mes/
cp ${LIBDIR}/${MES_ARCH}-mes/libmescc.s ${PREFIX}/lib/${MES_ARCH}-mes/
cp ${LIBDIR}/${MES_ARCH}-mes/${MES_ARCH}.M1 ${PREFIX}/lib/${MES_ARCH}-mes/
cp ${LIBDIR}/${MES_ARCH}-mes/crt1.o ${PREFIX}/lib/${MES_ARCH}-mes/
cp ${LIBDIR}/${MES_ARCH}-mes/crt1.s ${PREFIX}/lib/${MES_ARCH}-mes/
cp ${LIBDIR}/linux/${MES_ARCH}-mes/elf${ARCH_BITS}-footer-single-main.hex2 ${PREFIX}/lib/linux/${MES_ARCH}-mes/
cp ${LIBDIR}/linux/${MES_ARCH}-mes/elf${ARCH_BITS}-header.hex2 ${PREFIX}/lib/linux/${MES_ARCH}-mes/

# Install header files
cp include/alloca.h ${INCDIR}/alloca.h
cp include/argz.h ${INCDIR}/argz.h
cp include/ar.h ${INCDIR}/ar.h
cp include/assert.h ${INCDIR}/assert.h
cp include/ctype.h ${INCDIR}/ctype.h
cp include/dirent.h ${INCDIR}/dirent.h
cp include/dirstream.h ${INCDIR}/dirstream.h
cp include/dlfcn.h ${INCDIR}/dlfcn.h
cp include/endian.h ${INCDIR}/endian.h
cp include/errno.h ${INCDIR}/errno.h
cp include/fcntl.h ${INCDIR}/fcntl.h
cp include/features.h ${INCDIR}/features.h
cp include/float.h ${INCDIR}/float.h
cp include/getopt.h ${INCDIR}/getopt.h
cp include/grp.h ${INCDIR}/grp.h
cp include/inttypes.h ${INCDIR}/inttypes.h
cp include/libgen.h ${INCDIR}/libgen.h
cp include/limits.h ${INCDIR}/limits.h
cp include/locale.h ${INCDIR}/locale.h
cp include/math.h ${INCDIR}/math.h
cp include/memory.h ${INCDIR}/memory.h
cp include/pwd.h ${INCDIR}/pwd.h
cp include/setjmp.h ${INCDIR}/setjmp.h
cp include/signal.h ${INCDIR}/signal.h
cp include/stdarg.h ${INCDIR}/stdarg.h
cp include/stdbool.h ${INCDIR}/stdbool.h
cp include/stddef.h ${INCDIR}/stddef.h
cp include/stdint.h ${INCDIR}/stdint.h
cp include/stdio.h ${INCDIR}/stdio.h
cp include/stdlib.h ${INCDIR}/stdlib.h
cp include/stdnoreturn.h ${INCDIR}/stdnoreturn.h
cp include/string.h ${INCDIR}/string.h
cp include/strings.h ${INCDIR}/strings.h
cp include/termio.h ${INCDIR}/termio.h
cp include/time.h ${INCDIR}/time.h
cp include/unistd.h ${INCDIR}/unistd.h

cp include/arch/kernel-stat.h ${INCDIR}/arch/kernel-stat.h
cp include/arch/syscall.h ${INCDIR}/arch/syscall.h

cp include/linux/syscall.h ${INCDIR}/linux/syscall.h
cp include/linux/${MES_ARCH}/syscall.h ${INCDIR}/linux/${MES_ARCH}/syscall.h

cp include/mes/builtins.h ${INCDIR}/mes/builtins.h
cp include/mes/cc.h ${INCDIR}/mes/cc.h
catm ${INCDIR}/mes/config.h
cp include/mes/constants.h ${INCDIR}/mes/constants.h
cp include/mes/lib.h ${INCDIR}/mes/lib.h
cp include/mes/lib-cc.h ${INCDIR}/mes/lib-cc.h
cp include/mes/lib-mini.h ${INCDIR}/mes/lib-mini.h
cp include/mes/mes.h ${INCDIR}/mes/mes.h
cp include/mes/symbols.h ${INCDIR}/mes/symbols.h

cp include/sys/cdefs.h ${INCDIR}/sys/cdefs.h
cp include/sys/dir.h ${INCDIR}/sys/dir.h
cp include/sys/file.h ${INCDIR}/sys/file.h
cp include/sys/ioctl.h ${INCDIR}/sys/ioctl.h
cp include/sys/mman.h ${INCDIR}/sys/mman.h
cp include/sys/param.h ${INCDIR}/sys/param.h
cp include/sys/resource.h ${INCDIR}/sys/resource.h
cp include/sys/select.h ${INCDIR}/sys/select.h
cp include/sys/stat.h ${INCDIR}/sys/stat.h
cp include/sys/timeb.h ${INCDIR}/sys/timeb.h
cp include/sys/time.h ${INCDIR}/sys/time.h
cp include/sys/times.h ${INCDIR}/sys/times.h
cp include/sys/types.h ${INCDIR}/sys/types.h
cp include/sys/ucontext.h ${INCDIR}/sys/ucontext.h
cp include/sys/user.h ${INCDIR}/sys/user.h
cp include/sys/wait.h ${INCDIR}/sys/wait.h

cd ../..

# Checksums
if match x${UPDATE_CHECKSUMS} xTrue; then
    sha256sum -o ${pkg}.${ARCH}.checksums \
        /usr/bin/mes \
        /usr/bin/mes-m2 \
        /usr/bin/mescc.scm \
        /usr/lib/${MES_ARCH}-mes/crt1.s \
        /usr/lib/${MES_ARCH}-mes/crt1.o \
        /usr/lib/${MES_ARCH}-mes/${MES_ARCH}.M1 \
        /usr/lib/${MES_ARCH}-mes/libmescc.s \
        /usr/lib/${MES_ARCH}-mes/libc+tcc.s \
        /usr/lib/${MES_ARCH}-mes/libc.s \
        /usr/lib/${MES_ARCH}-mes/libmescc.a \
        /usr/lib/${MES_ARCH}-mes/libc+tcc.a \
        /usr/lib/${MES_ARCH}-mes/libc.a \
        /usr/lib/linux/${MES_ARCH}-mes/elf${ARCH_BITS}-header.hex2 \
        /usr/lib/linux/${MES_ARCH}-mes/elf${ARCH_BITS}-footer-single-main.hex2

    cp ${pkg}.${ARCH}.checksums ${SRCDIR}
else
    sha256sum -c ${pkg}.${ARCH}.checksums
fi

File /steps/mes-0.26/sources

Live-bootstrap source file is 'steps/mes-0.26/sources'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/mes-0.26/sources
https://mirrors.kernel.org/gnu/mes/mes-0.26.tar.gz 0f2210ad5896249466a0fc9a509e86c9a16db2b722741c6dfb5e8f7b33e385d4
https://github.com/Googulator/nyacc/releases/download/V1.00.2-lb1/nyacc-1.00.2-lb1.tar.gz 708c943f89c972910e9544ee077771acbd0a2c0fc6d33496fe158264ddb65327
https://archive.org/download/live-bootstrap-sources/nyacc-1.00.2-lb1.tar.gz 708c943f89c972910e9544ee077771acbd0a2c0fc6d33496fe158264ddb65327
https://files.bootstrapping.world/nyacc-1.00.2-lb1.tar.gz 708c943f89c972910e9544ee077771acbd0a2c0fc6d33496fe158264ddb65327

File /external/distfiles/mes-0.26.tar.gz

Live-bootstrap source file is 'distfiles/mes-0.26.tar.gz'.
URL: -- (Not shown)

File /external/distfiles/nyacc-1.00.2-lb1.tar.gz

Live-bootstrap source file is 'distfiles/nyacc-1.00.2-lb1.tar.gz'.
URL: -- (Not shown)

File /steps/mes-0.26/files/config.h

Live-bootstrap source file is 'steps/mes-0.26/files/config.h'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/mes-0.26/files/config.h
// SPDX-FileCopyrightText: 2020 fosslinux <fosslinux@aussies.space>
// SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>
//
// SPDX-License-Identifier: GPL-3.0-or-later

#undef SYSTEM_LIBC
#define MES_VERSION "0.26"

File /steps/mes-0.26/mes-0.26.x86.checksums

Live-bootstrap source file is 'steps/mes-0.26/mes-0.26.x86.checksums'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/mes-0.26/mes-0.26.x86.checksums
26db12128c08a6c83b8a96277949a46c8887f4c597df622f89ab4a52366aed8e  /usr/bin/mes
d3e1278cac1d7d3d5fb82042d635a43300a576059496adf9ea71d8e978de2f73  /usr/bin/mes-m2
84ac050c808a858cc58b17388a063d4a82a1bf975db4113202eb539c1eee84e1  /usr/bin/mescc.scm
234c264965116a24583dd569050adc766d7cc2da83b1db38085210f26031b70c  /usr/lib/x86-mes/crt1.s
494f184a87175abc485a898c250c3831b2b5dcf5aead46591e00b39d1c5529fc  /usr/lib/x86-mes/crt1.o
8afe05c3e1e8848b6f3e8de751524de5f69818eb8524659827918e3fddde3e1e  /usr/lib/x86-mes/x86.M1
771988b1df0dfa04bf5f1ab72af18c32b1a8205a07f10e5235d79dac0a072b00  /usr/lib/x86-mes/libmescc.s
aced8d8545f2274555c975e8db86e8804f3ba5a746e2a69bb02ca2905c257791  /usr/lib/x86-mes/libc+tcc.s
40c22be10018eaf9425f6b1b45e6ad2e4e169f8df12c4f03f271c892fe3422cd  /usr/lib/x86-mes/libc.s
52f697278ccdff5e457f27e10f465a91ab9858f0c6cee0683831cadb3109bbb7  /usr/lib/x86-mes/libmescc.a
ccf0ed21e4a8b6ed970f60c7ee7b1eb4cdd136e6a717632c538053d42231e155  /usr/lib/x86-mes/libc+tcc.a
c9c684c17a32b7adc1546a6ebba11a56ac88e6af2101f45cc0e8cd320555d735  /usr/lib/x86-mes/libc.a
82cac4353375a52efecc6fda1f8b37373988fe41ed197b63d8e906321b105d77  /usr/lib/linux/x86-mes/elf32-header.hex2
f9873d9aab12e70f24d97f8319e17a1e698ca60779ae9a6ab3ede648cd60fc61  /usr/lib/linux/x86-mes/elf32-footer-single-main.hex2

File /steps/tcc-0.9.26/pass1.kaem

Live-bootstrap source file is 'steps/tcc-0.9.26/pass1.kaem'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/tcc-0.9.26/pass1.kaem
#!/bin/sh

# SPDX-FileCopyrightText: 2021-22 fosslinux <fosslinux@aussies.space>
#
# SPDX-License-Identifier: GPL-3.0-or-later

set -ex

# Vars
MES_STACK=15000000
MES_ARENA=30000000
MES_MAX_ARENA=30000000
MES_LIB=${MES_PREFIX}/lib
MES_SOURCE=${MES_PREFIX}
MES=${BINDIR}/mes

TCC_TAR=tcc-0.9.26
TCC_PKG=tcc-0.9.26-1147-gee75a10c

# Check tarball checksums
checksum-transcriber sources
sha256sum -c sources.SHA256SUM

# Unpack
mkdir build

cd build
ungz --file ${DISTFILES}/${TCC_TAR}.tar.gz --output ${TCC_TAR}.tar
ungz --file ${DISTFILES}/${MES_PKG}.tar.gz --output ${MES_PKG}.tar
untar --non-strict --file ${TCC_TAR}.tar
simple-patch ${TCC_PKG}/tcctools.c \
    ../simple-patches/remove-fileopen.before ../simple-patches/remove-fileopen.after
simple-patch ${TCC_PKG}/tcctools.c \
    ../simple-patches/addback-fileopen.before ../simple-patches/addback-fileopen.after

untar --non-strict --file ${MES_PKG}.tar

# Create config.h
catm ${MES_PKG}/include/mes/config.h
catm ${TCC_PKG}/config.h
cd ${TCC_PKG}

if match ${ARCH} x86; then
    MES_ARCH=x86
    TCC_TARGET_ARCH=I386
    MES_LIBC_SUFFIX=gcc
    HAVE_LONG_LONG=0
fi
if match ${ARCH} amd64; then
    MES_ARCH=x86_64
    TCC_TARGET_ARCH=X86_64
    MES_LIBC_SUFFIX=gcc
    HAVE_LONG_LONG=1
fi
if match ${ARCH} riscv64; then
    MES_ARCH=riscv64
    TCC_TARGET_ARCH=RISCV64
    MES_LIBC_SUFFIX=tcc
    HAVE_LONG_LONG=1
fi


${MES} --no-auto-compile -e main ${BINDIR}/mescc.scm -- \
    -S \
    -o tcc.s \
    -I ${INCDIR} \
    -D BOOTSTRAP=1 \
    -D HAVE_LONG_LONG=${HAVE_LONG_LONG} \
    -I . \
    -D TCC_TARGET_${TCC_TARGET_ARCH}=1 \
    -D inline= \
    -D CONFIG_TCCDIR=\"${LIBDIR}/tcc\" \
    -D CONFIG_SYSROOT=\"/\" \
    -D CONFIG_TCC_CRTPREFIX=\"${LIBDIR}\" \
    -D CONFIG_TCC_ELFINTERP=\"/mes/loader\" \
    -D CONFIG_TCC_SYSINCLUDEPATHS=\"${PREFIX}/include/mes\" \
    -D TCC_LIBGCC=\"${LIBDIR}/libc.a\" \
    -D CONFIG_TCC_LIBTCC1_MES=0 \
    -D CONFIG_TCCBOOT=1 \
    -D CONFIG_TCC_STATIC=1 \
    -D CONFIG_USE_LIBGCC=1 \
    -D TCC_VERSION=\"0.9.26\" \
    -D ONE_SOURCE=1 \
    tcc.c
${MES} --no-auto-compile -e main ${BINDIR}/mescc.scm -- \
    --base-address 0x08048000 \
    -o tcc-mes \
    -L ${LIBDIR} \
    tcc.s \
    -l c+tcc
cp tcc-mes ${BINDIR}/
chmod 755 ${BINDIR}/tcc-mes

# test tcc-mes
tcc-mes -version

# Recompile the mes C library
cd ../${MES_PKG}

# Create unified libc file
cd lib
catm ../unified-libc.c ctype/isalnum.c ctype/isalpha.c ctype/isascii.c ctype/iscntrl.c ctype/isdigit.c ctype/isgraph.c ctype/islower.c ctype/isnumber.c ctype/isprint.c ctype/ispunct.c ctype/isspace.c ctype/isupper.c ctype/isxdigit.c ctype/tolower.c ctype/toupper.c dirent/closedir.c dirent/__getdirentries.c dirent/opendir.c linux/readdir.c linux/access.c linux/brk.c linux/chdir.c linux/chmod.c linux/clock_gettime.c linux/close.c linux/dup2.c linux/dup.c linux/execve.c linux/fcntl.c linux/fork.c linux/fsync.c linux/fstat.c linux/_getcwd.c linux/getdents.c linux/getegid.c linux/geteuid.c linux/getgid.c linux/getpid.c linux/getppid.c linux/getrusage.c linux/gettimeofday.c linux/getuid.c linux/ioctl.c linux/ioctl3.c linux/kill.c linux/link.c linux/lseek.c linux/lstat.c linux/malloc.c linux/mkdir.c linux/mknod.c linux/nanosleep.c linux/_open3.c linux/pipe.c linux/_read.c linux/readlink.c linux/rename.c linux/rmdir.c linux/setgid.c linux/settimer.c linux/setuid.c linux/signal.c linux/sigprogmask.c linux/symlink.c linux/stat.c linux/time.c linux/unlink.c linux/waitpid.c linux/wait4.c linux/${MES_ARCH}-mes-${MES_LIBC_SUFFIX}/_exit.c linux/${MES_ARCH}-mes-${MES_LIBC_SUFFIX}/syscall.c linux/${MES_ARCH}-mes-${MES_LIBC_SUFFIX}/_write.c math/ceil.c math/fabs.c math/floor.c mes/abtod.c mes/abtol.c mes/__assert_fail.c mes/assert_msg.c mes/__buffered_read.c mes/__init_io.c mes/cast.c mes/dtoab.c mes/eputc.c mes/eputs.c mes/fdgetc.c mes/fdgets.c mes/fdputc.c mes/fdputs.c mes/fdungetc.c mes/globals.c mes/itoa.c mes/ltoab.c mes/ltoa.c mes/__mes_debug.c mes/mes_open.c mes/ntoab.c mes/oputc.c mes/oputs.c mes/search-path.c mes/ultoa.c mes/utoa.c posix/alarm.c posix/buffered-read.c posix/execl.c posix/execlp.c posix/execv.c posix/execvp.c posix/getcwd.c posix/getenv.c posix/isatty.c posix/mktemp.c posix/open.c posix/raise.c posix/sbrk.c posix/setenv.c posix/sleep.c posix/unsetenv.c posix/wait.c posix/write.c stdio/clearerr.c stdio/fclose.c stdio/fdopen.c stdio/feof.c stdio/ferror.c stdio/fflush.c stdio/fgetc.c stdio/fgets.c stdio/fileno.c stdio/fopen.c stdio/fprintf.c stdio/fputc.c stdio/fputs.c stdio/fread.c stdio/freopen.c stdio/fscanf.c stdio/fseek.c stdio/ftell.c stdio/fwrite.c stdio/getc.c stdio/getchar.c stdio/perror.c stdio/printf.c stdio/putc.c stdio/putchar.c stdio/remove.c stdio/snprintf.c stdio/sprintf.c stdio/sscanf.c stdio/ungetc.c stdio/vfprintf.c stdio/vfscanf.c stdio/vprintf.c stdio/vsnprintf.c stdio/vsprintf.c stdio/vsscanf.c stdlib/abort.c stdlib/abs.c stdlib/alloca.c stdlib/atexit.c stdlib/atof.c stdlib/atoi.c stdlib/atol.c stdlib/calloc.c stdlib/__exit.c stdlib/exit.c stdlib/free.c stdlib/mbstowcs.c stdlib/puts.c stdlib/qsort.c stdlib/realloc.c stdlib/strtod.c stdlib/strtof.c stdlib/strtol.c stdlib/strtold.c stdlib/strtoll.c stdlib/strtoul.c stdlib/strtoull.c string/bcmp.c string/bcopy.c string/bzero.c string/index.c string/memchr.c string/memcmp.c string/memcpy.c string/memmem.c string/memmove.c string/memset.c string/rindex.c string/strcat.c string/strchr.c string/strcmp.c string/strcpy.c string/strcspn.c string/strdup.c string/strerror.c string/strlen.c string/strlwr.c string/strncat.c string/strncmp.c string/strncpy.c string/strpbrk.c string/strrchr.c string/strspn.c string/strstr.c string/strupr.c stub/atan2.c stub/bsearch.c stub/chown.c stub/__cleanup.c stub/cos.c stub/ctime.c stub/exp.c stub/fpurge.c stub/freadahead.c stub/frexp.c stub/getgrgid.c stub/getgrnam.c stub/getlogin.c stub/getpgid.c stub/getpgrp.c stub/getpwnam.c stub/getpwuid.c stub/gmtime.c stub/ldexp.c stub/localtime.c stub/log.c stub/mktime.c stub/modf.c stub/mprotect.c stub/pclose.c stub/popen.c stub/pow.c stub/rand.c stub/rewind.c stub/setbuf.c stub/setgrent.c stub/setlocale.c stub/setvbuf.c stub/sigaction.c stub/sigaddset.c stub/sigblock.c stub/sigdelset.c stub/sigemptyset.c stub/sigsetmask.c stub/sin.c stub/sys_siglist.c stub/system.c stub/sqrt.c stub/strftime.c stub/times.c stub/ttyname.c stub/umask.c stub/utime.c ${MES_ARCH}-mes-${MES_LIBC_SUFFIX}/setjmp.c
cd ..

# crt1.o
tcc-mes -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o ${LIBDIR}/crt1.o lib/linux/${MES_ARCH}-mes-${MES_LIBC_SUFFIX}/crt1.c

catm ${LIBDIR}/crtn.o
catm ${LIBDIR}/crti.o
if match ${ARCH} x86; then
    # crtn.o
    tcc-mes -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o ${LIBDIR}/crtn.o lib/linux/${MES_ARCH}-mes-gcc/crtn.c

    # crti.o
    tcc-mes -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o ${LIBDIR}/crti.o lib/linux/${MES_ARCH}-mes-gcc/crti.c
fi

# libc+gcc.a
tcc-mes -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o unified-libc.o unified-libc.c
tcc-mes -ar cr ${LIBDIR}/libc.a unified-libc.o

# libtcc1.a
mkdir ${LIBDIR}/tcc
tcc-mes -c -D HAVE_CONFIG_H=1 -D HAVE_LONG_LONG=1 -D HAVE_FLOAT=1 -I include -I include/linux/${MES_ARCH} -o libtcc1.o lib/libtcc1.c
if match ${ARCH} riscv64; then
    tcc-mes -c -D HAVE_CONFIG_H=1 -D HAVE_LONG_LONG=1 -D HAVE_FLOAT=1 -I include -I include/linux/${MES_ARCH} -o lib-arm64.o ../${TCC_PKG}/lib/lib-arm64.c
    tcc-mes -ar cr ${LIBDIR}/tcc/libtcc1.a libtcc1.o lib-arm64.o
else
    tcc-mes -ar cr ${LIBDIR}/tcc/libtcc1.a libtcc1.o
fi

# libgetopt.a
tcc-mes -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} lib/posix/getopt.c
tcc-mes -ar cr ${LIBDIR}/libgetopt.a getopt.o

cd ../${TCC_PKG}

# boot0 (ref comments here for all boot*)
# compile
tcc-mes \
    -g \
    -v \
    -static \
    -o tcc-boot0 \
    -D BOOTSTRAP=1 \
    -D HAVE_FLOAT=1 \
    -D HAVE_BITFIELD=1 \
    -D HAVE_LONG_LONG=1 \
    -D HAVE_SETJMP=1 \
    -I . \
    -I ${PREFIX}/include/mes \
    -D TCC_TARGET_${TCC_TARGET_ARCH}=1 \
    -D CONFIG_TCCDIR=\"${LIBDIR}/tcc\" \
    -D CONFIG_TCC_CRTPREFIX=\"${LIBDIR}\" \
    -D CONFIG_TCC_ELFINTERP=\"/mes/loader\" \
    -D CONFIG_TCC_LIBPATHS=\"${LIBDIR}:${LIBDIR}/tcc\" \
    -D CONFIG_TCC_SYSINCLUDEPATHS=\"${PREFIX}/include/mes\" \
    -D TCC_LIBGCC=\"${LIBDIR}/libc.a\" \
    -D TCC_LIBTCC1=\"libtcc1.a\" \
    -D CONFIG_TCCBOOT=1 \
    -D CONFIG_TCC_STATIC=1 \
    -D CONFIG_USE_LIBGCC=1 \
    -D TCC_VERSION=\"0.9.26\" \
    -D ONE_SOURCE=1 \
    -L . \
    -L ${LIBDIR} \
    tcc.c
# Install
cp tcc-boot0 ${BINDIR}/
chmod 755 ${BINDIR}/tcc-boot0
cd ../${MES_PKG}
# Recompile libc: crt{1,n,i}, libtcc.a, libc.a
tcc-boot0 -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o ${LIBDIR}/crt1.o lib/linux/${MES_ARCH}-mes-${MES_LIBC_SUFFIX}/crt1.c
if match ${ARCH} x86; then
    tcc-boot0 -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o ${LIBDIR}/crtn.o lib/linux/${MES_ARCH}-mes-gcc/crtn.c
    tcc-boot0 -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o ${LIBDIR}/crti.o lib/linux/${MES_ARCH}-mes-gcc/crti.c
fi

tcc-boot0 -c -D HAVE_CONFIG_H=1 -D HAVE_LONG_LONG=1 -D HAVE_FLOAT=1 -I include -I include/linux/${MES_ARCH} -o libtcc1.o lib/libtcc1.c
if match ${ARCH} riscv64; then
    tcc-boot0 -c -D HAVE_CONFIG_H=1 -D HAVE_LONG_LONG=1 -D HAVE_FLOAT=1 -I include -I include/linux/${MES_ARCH} -o lib-arm64.o ../${TCC_PKG}/lib/lib-arm64.c
    tcc-boot0 -ar cr ${LIBDIR}/tcc/libtcc1.a libtcc1.o lib-arm64.o
else
    tcc-boot0 -ar cr ${LIBDIR}/tcc/libtcc1.a libtcc1.o
fi

tcc-boot0 -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o unified-libc.o unified-libc.c
tcc-boot0 -ar cr ${LIBDIR}/libc.a unified-libc.o
cd ../${TCC_PKG}

# Test boot0
tcc-boot0 -version

# boot1
tcc-boot0 \
    -g \
    -v \
    -static \
    -o tcc-boot1 \
    -D BOOTSTRAP=1 \
    -D HAVE_FLOAT=1 \
    -D HAVE_BITFIELD=1 \
    -D HAVE_LONG_LONG=1 \
    -D HAVE_SETJMP=1 \
    -I . \
    -I ${PREFIX}/include/mes \
    -D TCC_TARGET_${TCC_TARGET_ARCH}=1 \
    -D CONFIG_TCCDIR=\"${LIBDIR}/tcc\" \
    -D CONFIG_TCC_CRTPREFIX=\"${LIBDIR}\" \
    -D CONFIG_TCC_ELFINTERP=\"/mes/loader\" \
    -D CONFIG_TCC_LIBPATHS=\"${LIBDIR}:${LIBDIR}/tcc\" \
    -D CONFIG_TCC_SYSINCLUDEPATHS=\"${PREFIX}/include/mes\" \
    -D TCC_LIBGCC=\"${LIBDIR}/libc.a\" \
    -D TCC_LIBTCC1=\"libtcc1.a\" \
    -D CONFIG_TCCBOOT=1 \
    -D CONFIG_TCC_STATIC=1 \
    -D CONFIG_USE_LIBGCC=1 \
    -D TCC_VERSION=\"0.9.26\" \
    -D ONE_SOURCE=1 \
    -L . \
    tcc.c
cp tcc-boot1 ${BINDIR}
chmod 755 ${BINDIR}/tcc-boot1
cd ../${MES_PKG}
tcc-boot1 -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o ${LIBDIR}/crt1.o lib/linux/${MES_ARCH}-mes-${MES_LIBC_SUFFIX}/crt1.c
if match ${ARCH} x86; then
    tcc-boot1 -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o ${LIBDIR}/crtn.o lib/linux/${MES_ARCH}-mes-gcc/crtn.c
    tcc-boot1 -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o ${LIBDIR}/crti.o lib/linux/${MES_ARCH}-mes-gcc/crti.c
fi

tcc-boot1 -c -D HAVE_CONFIG_H=1 -D HAVE_LONG_LONG=1 -D HAVE_FLOAT=1 -I include -I include/linux/${MES_ARCH} -o libtcc1.o lib/libtcc1.c
if match ${ARCH} riscv64; then
    tcc-boot1 -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o lib-arm64.o ../${TCC_PKG}/lib/lib-arm64.c
    tcc-boot1 -ar cr ${LIBDIR}/tcc/libtcc1.a libtcc1.o lib-arm64.o
else
    tcc-boot1 -ar cr ${LIBDIR}/tcc/libtcc1.a libtcc1.o
fi

tcc-boot1 -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o unified-libc.o unified-libc.c
tcc-boot1 -ar cr ${LIBDIR}/libc.a unified-libc.o
cd ../${TCC_PKG}

# Test boot1
tcc-boot1 -version

# boot2
tcc-boot1 \
    -g \
    -v \
    -static \
    -o tcc-boot2 \
    -D BOOTSTRAP=1 \
    -D HAVE_BITFIELD=1 \
    -D HAVE_FLOAT=1 \
    -D HAVE_LONG_LONG=1 \
    -D HAVE_SETJMP=1 \
    -I . \
    -I ${PREFIX}/include/mes \
    -D TCC_TARGET_${TCC_TARGET_ARCH}=1 \
    -D CONFIG_TCCDIR=\"${LIBDIR}/tcc\" \
    -D CONFIG_TCC_CRTPREFIX=\"${LIBDIR}\" \
    -D CONFIG_TCC_ELFINTERP=\"/mes/loader\" \
    -D CONFIG_TCC_LIBPATHS=\"${LIBDIR}:${LIBDIR}/tcc\" \
    -D CONFIG_TCC_SYSINCLUDEPATHS=\"${PREFIX}/include/mes\" \
    -D TCC_LIBGCC=\"${LIBDIR}/libc.a\" \
    -D TCC_LIBTCC1=\"libtcc1.a\" \
    -D CONFIG_TCCBOOT=1 \
    -D CONFIG_TCC_STATIC=1 \
    -D CONFIG_USE_LIBGCC=1 \
    -D TCC_VERSION=\"0.9.26\" \
    -D ONE_SOURCE=1 \
    -L . \
    tcc.c
cp tcc-boot2 ${BINDIR}
chmod 755 ${BINDIR}/tcc-boot2
cd ../${MES_PKG}
tcc-boot2 -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o ${LIBDIR}/crt1.o lib/linux/${MES_ARCH}-mes-${MES_LIBC_SUFFIX}/crt1.c
if match ${ARCH} x86; then
    tcc-boot2 -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o ${LIBDIR}/crtn.o lib/linux/${MES_ARCH}-mes-gcc/crtn.c
    tcc-boot2 -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o ${LIBDIR}/crti.o lib/linux/${MES_ARCH}-mes-gcc/crti.c
fi

tcc-boot2 -c -D HAVE_CONFIG_H=1 -D HAVE_LONG_LONG=1 -D HAVE_FLOAT=1 -I include -I include/linux/${MES_ARCH} -o libtcc1.o lib/libtcc1.c
if match ${ARCH} riscv64; then
    tcc-boot2 -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o lib-arm64.o ../${TCC_PKG}/lib/lib-arm64.c
    tcc-boot2 -ar cr ${LIBDIR}/tcc/libtcc1.a libtcc1.o lib-arm64.o
else
    tcc-boot2 -ar cr ${LIBDIR}/tcc/libtcc1.a libtcc1.o
fi

tcc-boot2 -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} -o unified-libc.o unified-libc.c
tcc-boot2 -ar cr ${LIBDIR}/libc.a unified-libc.o
cd ../${TCC_PKG}

# Test boot2
tcc-boot2 -version

# We have our final tcc 0.9.26!
cp ${BINDIR}/tcc-boot2 ${BINDIR}/tcc
chmod 755 ${BINDIR}/tcc
rm ${BINDIR}/tcc-boot2
cp ${BINDIR}/tcc ${BINDIR}/tcc-0.9.26
chmod 755 ${BINDIR}/tcc-0.9.26

# Also recompile getopt, we don't need to do this during the boot* stages
# because nothing is linked against it
cd ../${MES_PKG}
tcc -c -D HAVE_CONFIG_H=1 -I include -I include/linux/${MES_ARCH} lib/posix/getopt.c
tcc -ar cr ${LIBDIR}/libgetopt.a getopt.o

cd ../..

# Checksums
if match x${UPDATE_CHECKSUMS} xTrue; then
    sha256sum -o ${pkg}.${ARCH}.checksums \
        /usr/bin/tcc-mes \
        /usr/bin/tcc-boot0 \
        /usr/bin/tcc-boot1 \
        /usr/bin/tcc \
        /usr/lib/mes/libc.a \
        /usr/lib/mes/libgetopt.a \
        /usr/lib/mes/crt1.o \
        /usr/lib/mes/crti.o \
        /usr/lib/mes/crtn.o \
        /usr/lib/mes/tcc/libtcc1.a

    cp ${pkg}.${ARCH}.checksums ${SRCDIR}
else
    sha256sum -c ${pkg}.${ARCH}.checksums
fi

File /steps/tcc-0.9.26/sources

Live-bootstrap source file is 'steps/tcc-0.9.26/sources'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/tcc-0.9.26/sources
https://lilypond.org/janneke/tcc/tcc-0.9.26-1147-gee75a10c.tar.gz 6b8cbd0a5fed0636d4f0f763a603247bc1935e206e1cc5bda6a2818bab6e819f tcc-0.9.26.tar.gz

File /external/distfiles/tcc-0.9.26.tar.gz

Live-bootstrap source file is 'distfiles/tcc-0.9.26.tar.gz'.
URL: -- (Not shown)

File /steps/tcc-0.9.26/simple-patches/remove-fileopen.before

Live-bootstrap source file is 'steps/tcc-0.9.26/simple-patches/remove-fileopen.before'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/tcc-0.9.26/simple-patches/remove-fileopen.before
    if (ret == 1)
        return ar_usage(ret);

    if ((fh = fopen(argv[i_lib], "wb")) == NULL)
    {
        fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_lib]);
        goto the_end;
    }

File /steps/tcc-0.9.26/simple-patches/remove-fileopen.after

Live-bootstrap source file is 'steps/tcc-0.9.26/simple-patches/remove-fileopen.after'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/tcc-0.9.26/simple-patches/remove-fileopen.after
    if (ret == 1)
        return ar_usage(ret);

File /steps/tcc-0.9.26/simple-patches/addback-fileopen.before

Live-bootstrap source file is 'steps/tcc-0.9.26/simple-patches/addback-fileopen.before'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/tcc-0.9.26/simple-patches/addback-fileopen.before
    // write header

File /steps/tcc-0.9.26/simple-patches/addback-fileopen.after

Live-bootstrap source file is 'steps/tcc-0.9.26/simple-patches/addback-fileopen.after'.
URL: https://github.com/fosslinux/live-bootstrap/blob/0e6133eed2a31a6bdaac03f2c1845a3908a2faff/steps/tcc-0.9.26/simple-patches/addback-fileopen.after
    if ((fh = fopen(argv[i_lib], "wb")) == NULL)
    {
        fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_lib]);
        goto the_end;
    }

    // write header

File /usr/lib/mes/libc.a.tmp

(Source not found at '(null)')

File /usr/lib/mes/tcc/libtcc1.a.tmp

(Source not found at '(null)')

File /usr/lib/mes/libgetopt.a.tmp

(Source not found at '(null)')

Output files

Executables files: Intermediary files (not from sources and used): Produced (not from source and also not used):

Parse program

Below the Bash script run_chroot to produce the trace.txt file.

#! /bin/bash

rm -rf rootfs
mkdir rootfs
cd rootfs
cp -r -t . ../../live-bootstrap/seed/stage0-posix/*
cp -rf -t . ../../live-bootstrap/seed/*.kaem
cp -r -t . ../../live-bootstrap/seed/*.checksums
cp -r -t . ../../live-bootstrap/seed/*.c
cp -r -t . ../../live-bootstrap/steps
mkdir external
cp -r -t external ../../live-bootstrap/distfiles
cp ../replacement/steps/bootstrap.cfg steps
cd ..
strace -f -o trace.txt -e trace=open,openat,close,chmod,chdir,dup,fcntl,link,linkat,unlink,fork,execve chroot rootfs /bootstrap-seeds/POSIX/x86/kaem-optional-seed
Below the version of the scan_trace.cpp program is given that is used to produce this page.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

// ---------------------------------

char *copystr(const char *str)
{
    char *new_str = (char*)malloc(strlen(str) + 1);
    strcpy(new_str, str);
    return new_str;
}

FILE *fout = stdout;

#define MAX_FILENAME_LEN 500

// ---------------------------------

class SubModule
{
public:
    char *path;
    char *url;
    SubModule *next;
    SubModule(const char *p, const char *u, SubModule *n) : next(n)
    {
        path = copystr(p);
        url = copystr(u);
    }
};

SubModule* lbs_subModules = 0;
SubModule* dist_subModules = 0;

void addSubModule(const char *path, const char *url) { lbs_subModules = new SubModule(path, url, lbs_subModules); }
void addDistSubModule(const char *path, const char *url) { dist_subModules = new SubModule(path, url, dist_subModules); }

const char *source_dir = "../live-bootstrap/";
size_t len_source_dir;

void read_commit_hash(const char *path, char *commit)
{
    FILE *f = fopen(path, "r");
    if (f == 0)
        commit[0] = '\0';
    else
    {
        if (fgets(commit, 41, f))
            commit[40] = '\0';
        else
            commit[0] = '\0';
        fclose(f);
    }
    fprintf(fout, "Head file: '%s': %s\n", path, commit);
}

void read_sub_modules(const char *fn, char *modules_dir)
{
    char complete_path[MAX_FILENAME_LEN+1];
    strcpy(complete_path, fn);
    char *s_path = complete_path + strlen(complete_path);
    char *m_path = modules_dir + strlen(modules_dir);
    strcpy(s_path, ".gitmodules");
    FILE *f = fopen(complete_path, "r");
    if (f == 0)
        return;
    //fprintf(fout, "Parsing %s %s\n", complete_path, modules_dir);
    
    char path[MAX_FILENAME_LEN+1];
    char url[MAX_FILENAME_LEN+1];
    
    while (fgets(path, MAX_FILENAME_LEN, f))
    {
        if (!strncmp(path, "\tpath = ", 8) == 0)
            continue;
        if (fgets(url, MAX_FILENAME_LEN, f) && strncmp(url, "\turl = ", 7) == 0)
        {
            //fprintf(fout, "1: %s2: %s", path, url);
            while (path[strlen(path)-1] < ' ') path[strlen(path)-1] = '\0';
            while (url[strlen(url)-1] < ' ') url[strlen(url)-1] = '\0';
            char *s = strstr(url, ".git");
            if (s != 0) *s = '\0';
            sprintf(s_path, "%s/", path + 8);
            char *source = complete_path + len_source_dir;

            sprintf(m_path, "%s/HEAD", path + 8);
            char commit[41];
            read_commit_hash(modules_dir, commit);

            //fprintf(fout, "Head file: '%s': %s\n", modules_dir, commit);
            sprintf(m_path, "%s/modules/", path + 8);

            int len = strlen(url);
            char complete_url[MAX_FILENAME_LEN+1];
            if (strncmp(url + 7, "https://github.com/", 19) == 0)
            {
                if (commit[0] == '\0')
                    sprintf(complete_url, "%s/blob/main/%%s", url + 7);
                else
                    sprintf(complete_url, "%s/blob/%s/%%s", url + 7, commit);
            }
            else if (strncmp(url + 7, "https://git.savannah.nongnu.org/git/", 36) == 0)
            {
                if (commit[0] == '\0')
                    sprintf(complete_url, "https://git.savannah.nongnu.org/gitweb/?p=%s.git;a=blob;f=%%s", url + 43);
                else
                    sprintf(complete_url, "https://git.savannah.nongnu.org/gitweb/?p=%s.git;a=blob;f=%%s;id=%s", url + 43, commit);
            }
            else
                sprintf(complete_url, "%s%%s", url + 7);
            //printf("Submodule %s %s|\n", source, complete_url);
            addSubModule(source, complete_url);
            read_sub_modules(complete_path, modules_dir);
        }
    }
}

char live_bootstrap_commit[41];

void init_subModules()
{
    // Root directory:
    char path[MAX_FILENAME_LEN+1];
    snprintf(path, MAX_FILENAME_LEN, "%s.git/refs/heads/master", source_dir);
    read_commit_hash(path, live_bootstrap_commit);
    snprintf(path, MAX_FILENAME_LEN, "https://github.com/fosslinux/live-bootstrap/blob/%s/%%s",
        live_bootstrap_commit[0] == '\0' ? "master" : live_bootstrap_commit);
    addSubModule("", path);

    // Sub modules
    char modules_dir[MAX_FILENAME_LEN+1];
    snprintf(modules_dir, MAX_FILENAME_LEN, "%s.git/modules/", source_dir);
    read_sub_modules(source_dir, modules_dir);

    // Exclude some directories
    addSubModule("distfiles", "--");
    
    // Extra for files that have been unpacked from distribution
    addDistSubModule("/steps/tcc-0.9.26/build/mes-0.26/", "https://git.savannah.gnu.org/cgit/mes.git/tree/%s?h=v0.26");
    addDistSubModule("/steps/tcc-0.9.26/build/tcc-0.9.26-1147-gee75a10c/", "https://github.com/TinyCC/tinycc/tree/d5e22108a0dc48899e44a158f91d5b3215eb7fe6/%s");
    addDistSubModule("/steps/mes-0.26/build/mes-0.26/", "https://git.savannah.gnu.org/cgit/mes.git/tree/%s?h=v0.26");
    addDistSubModule("/steps/mes-0.26/build/nyacc-1.00.2/", "https://git.savannah.gnu.org/cgit/nyacc.git/tree/%s?h=V1.00.2");

    /*
    addSubModule("steps/bzip2-1.0.8/build/bzip2-1.0.8/", "https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/%s");
    addSubModule("steps/tcc-0.9.26/build/mes-0.24.2/", "https://git.savannah.gnu.org/cgit/mes.git/tree/%s?h=v0.24.2");
    addSubModule("steps/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/", "https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/%s");
    addSubModule("steps/tcc-0.9.27/build/mes-0.24.2/", "https://git.savannah.gnu.org/cgit/mes.git/tree/%s?h=v0.24.2");
    addSubModule("steps/tcc-0.9.27/build/tcc-0.9.27/", "https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/%s");
    addSubModule("steps/tcc-0.9.27/build/tcc-0.9.27/", "https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/%s");
    addSubModule("steps/make-3.82/build/make-3.82/", "https://git.savannah.gnu.org/cgit/make.git/tree/%s?h=3.82");
    addSubModule("steps/coreutils-5.0/build/coreutils-5.0/", "https://git.savannah.gnu.org/cgit/coreutils.git/tree/%s?h=COREUTILS-5_0");
    addSubModule("steps/tar-1.12/build/tar-1.12", "https://git.savannah.gnu.org/cgit/tar.git/tree/%s?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e");
    addSubModule("steps/sed-4.0.9/build/sed-4.0.9/", "https://git.savannah.gnu.org/cgit/sed.git/tree/%s?id=9c9919efe2166efd32409054005619062624226c");
    addSubModule("steps/bash-2.05b/build/bash-2.05b/", "https://git.savannah.gnu.org/cgit/bash.git/tree/%s?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55");
    addSubModule("steps/mes-0.24.2/build/mes-0.24.2/", "https://git.savannah.gnu.org/cgit/mes.git/tree/%s?h=v0.24.2");
    */
}

char *get_url(const char *path, SubModule *subModules)
{
    for (SubModule *subModule = subModules; subModule != 0; subModule = subModule->next)
    {
        size_t path_len = strlen(subModule->path);
        if (strncmp(path, subModule->path, path_len) == 0)
        {
            char url[MAX_FILENAME_LEN+1];
            snprintf(url, MAX_FILENAME_LEN, subModule->url, path + path_len);
            return copystr(url);
        }
    }
    return 0;
}

// ---------------------------------

class File;
class Action;
class Process;



class LineInFile
{
public:
    const char *text;
    File *file;
    long line;
    LineInFile *next;
    LineInFile(const char *t, File *f, long l) : text(copystr(t)), file(f), line(l), next(0) {}
    LineInFile(LineInFile *lif, int offset = 0) : text(lif->text + offset), file(lif->file), line(lif->line), next(0) {}
};

class MergeChild;

int nr_files = 0;
class File
{
public:
    char *name;
    int nr;
    bool is_source;
    char *source_name;
    char *url;
    File *copy_from;
    Action *actions;
    File *next;
    
    File(const char *fn) : is_source(false), source_name(0), url(0), copy_from(0), actions(0), next(0)
    {
        name = copystr(fn);
        nr = nr_files++;
    }

    void init_source();

    bool exec_before_created();
    
    bool used_as_input();
    
    bool produced_and_not_removed();
};

File *files = 0;

int nr_processes = 0;
class Process
{
public:
    int nr;
    unsigned long pid;
    Process *parent;
    Action *actions;
    Process *next;
    
    Process(unsigned long _pid) : pid(_pid), parent(0), actions(0), next(0)
    {
        nr = ++nr_processes;
    }
    bool hasInputUseOf(File *f);
    Action *lastOpenAction(int handle);
};

Process *all_processes = 0;
Process **ref_next = &all_processes;
Process *next_process(unsigned long pid)
{
    Process *process = new Process(pid);
    *ref_next = process;
    ref_next = &process->next;
    return process;
}

Process *find_process(unsigned long pid)
{
    Process *cached_process = 0;
    if (cached_process != 0 && cached_process->pid == pid)
        return cached_process;
    for (Process *process = all_processes; process != 0; process = process->next)
        if (process->pid == pid)
        {
            cached_process = process;
            return process;
        }
    return 0;
}

class MergeChild
{
public:
    File *child;
    MergeChild *next;
    
    MergeChild(File *file) : child(file), next(0) {}
};

File *get_file(const char *full_fn /*, bool use_alias = true*/)
{
    File **ref = &files;
    for (; *ref != 0; ref = &(*ref)->next)
        if (strcmp((*ref)->name, full_fn) == 0)
            return /*(*ref)->alias != 0 && use_alias ? (*ref)->alias :*/ (*ref);
    *ref = new File(full_fn);
    return *ref;
}



    

unsigned long read_unsigned_long(char *&s)
{
    unsigned long result = 0;
    if (*s == '0')
    {
        for (s++; '0' <= *s && *s <= '7'; s++)
            result = 8 * result + *s - '0';
        return result;
    }
    for (; '0' <= *s && *s <= '9'; s++)
        result = 10 * result + *s - '0';
    return result;
}

long read_long(char *&s)
{
    long sign = 1;
    long result = 0;
    if (*s == '-')
    {
        sign = -1;
        s++;
    }
    for (; '0' <= *s && *s <= '9'; s++)
        result = 10 * result + *s - '0';
    return sign * result;
}

int indent_depth = 0;
void indent(FILE *fout) { fprintf(fout, "%*.*s", indent_depth, indent_depth, ""); }

FILE *fout_usage = 0;

class Action
{
    public:
    char kind; // one of 'e' - execute, 'o' - open, 'r' - removed, 'c' - change mode, 'E' - execute child
    bool o_rdonly;
    bool o_wronly;
    bool o_rdwr;
    bool o_creat;
    bool o_trunc;
    bool o_excl;
    int file_handle;
    bool is_closed;
    int mode;
    bool from_archive;
    Process *child_process;
    
    File *file;
    Process *process;
    
    Action *next_in_process;
    Action *next_on_file;
    
    Action (File *_file, Process *_process, char _kind)
    : kind(_kind), 
      o_rdonly(false), o_wronly(false), o_rdwr(false), o_creat(false), o_trunc(false), o_excl(false), file_handle(-1), is_closed(false),
      mode(0),
      from_archive(false),
      child_process(0), 
      file(_file), process(_process), next_in_process(0), next_on_file(0)
    {
        Action **ref_action_in_process = &process->actions;
        while (*ref_action_in_process != 0) ref_action_in_process = &(*ref_action_in_process)->next_in_process;
        *ref_action_in_process = this;
        if (file != 0)
        {
            Action **ref_action_on_file = &file->actions;
            while (*ref_action_on_file != 0) ref_action_on_file = &(*ref_action_on_file)->next_on_file;
            *ref_action_on_file = this;
        }
    }
    
    const char *oper_name()
    {
        return kind == 'e' ? "Executes" :
               kind == 'r' ? "Delete" :
               is_produced() ? "Produces" :
               kind != 'o' ? 0 :
               o_rdonly ? "Uses as input" :
               o_wronly ? "Writes" :
               o_rdwr ? "Modifies" :
               "Uses";
    }   
    
    bool is_produced() { return kind == 'o' && (o_creat || ((o_wronly || o_rdwr) && o_trunc)); }
};

bool Process::hasInputUseOf(File *f)
{
    for (Action *action = actions; action != 0; action = action->next_in_process)
        if (action->file == f && action->o_rdonly)
            return true;
    return false;
}

Action *Process::lastOpenAction(int handle)
{
    Action *last_open_action = 0;
    for (Action *action = actions; action != 0; action = action->next_in_process)
        if (action->kind == 'o' && action->file_handle == handle)
            last_open_action = action;
    return last_open_action;
}

void File::init_source()
{
    if (actions == 0 || actions->next_on_file != 0)
        return;
    
    if (actions->kind == 'o' && actions->is_produced())
    {
        if (actions->process->actions->kind == 'e' && strcmp(actions->process->actions->file->name, "/usr/bin/untar") == 0)
        {
            actions->from_archive = true;
            url = get_url(name, dist_subModules);
        }
    }
    else if (actions->kind == 'e' || actions->kind == 'o')
    {
        is_source = true;
        char *n = name;
        if (n[0] == '/')
            n++;
        if (strncmp(n, "external/distfiles/", 19) == 0)
            n += 9;
    
        static const char *paths[] = { "replacement/", "*seed/", "*seed/stage0-posix/", "*"};
        for (size_t i = 0; i < sizeof(paths)/sizeof(paths[0]); i++)
        {
            char poss_source_name[MAX_FILENAME_LEN];
            if (paths[i][0] == '*')
            {
                strcpy(poss_source_name, source_dir);
                strcat(poss_source_name, paths[i] + 1);
            }
            else
                strcpy(poss_source_name, paths[i]);
            strcat(poss_source_name, n);
            if (access(poss_source_name, R_OK) == 0)
            {
                source_name = copystr(poss_source_name);
                break;
            }
        }
    
        if (source_name != 0)
            url = get_url(source_name + len_source_dir, lbs_subModules);
    }
}

bool File::exec_before_created()
{
    bool is_created = false;
    for (Action *action = actions; action != 0; action = action->next_on_file)
        if (action->kind == 'o' && action->o_creat && !action->from_archive)
            is_created = true;
        else if (action->kind == 'r')
            is_created = false;
        else if (action->kind == 'e' && !is_created)
            return true;
    return false;
}

bool File::used_as_input()
{
    return actions != 0 && (actions->o_rdonly || actions->o_rdwr);
}

bool File::produced_and_not_removed()
{
    if (used_as_input())
        return false;
    bool is_created = false;
    for (Action *action = actions; action != 0; action = action->next_on_file)
        if (action->kind == 'o' && (action->o_creat || action->o_wronly || (action->o_rdwr && action->o_trunc)) && !action->from_archive)
            is_created = true;
        else if (action->kind == 'r')
            is_created = false;
        else if (action->kind == 'e' && !is_created)
            return false;
    return is_created;
}

// ----------------------------------



bool accept_string(const char *str, char *&s)
{
    char *t = s;
    while (*str != '\0' && *t != '\0')
    {
        if (*str != *t)
            return false;
        t++;
        if (*str == ' ')
        {
            while (*t == ' ')
                t++;
        }
        str++;
    }
    s = t;
    return true;
}

bool parse_filename(char *filename, char *&s)
{
    if (*s != '"')
        return false;
    s++;
    for (int i = 0; i < MAX_FILENAME_LEN; i++)
    {
        if (*s == '"')
        {
            filename[i] = '\0';
            s++;
            return true;
        }
        while (s[0] == '/' && s[1] == '/')
            s++;
        filename[i] = *s++;
    }
    
    fprintf(fout, "file name too long\n");
    exit(-1);
    return false;
}

char cd_path[MAX_FILENAME_LEN] = "/";

void add_cd_path(char *filename)
{
    char buf[2*MAX_FILENAME_LEN+1];

    //fprintf(log_file, "add_cd_path %s %s => ", cd_path, filename);
    if (filename[0] == '/')
    {
        char *s = filename;
        while (s[1] == '/')
            s++;
        strcpy(buf, s);
        //fprintf(fout, "add_cd_path %s %s => %s\n", cd_path, filename, buf);
        strcpy(filename, buf);
        return;
    }
    strcpy(buf, cd_path);
    int i = strlen(buf);
    while (i > 0 && buf[i-1] == '/')
        i--;
    char *f = filename;
    while (f[0] != '\0')
    {
        if (f[0] == '.' && (f[1] == '\0' || f[1] == '/'))
        {
            f++;
            while (f[0] == '/')
                f++;
        }
        else if (f[0] == '.' && f[1] == '.' && (f[2] == '\0' || f[2] == '/'))
        {
            f += 2;
            while (f[0] == '/')
                f++;
            while (i > 0 && buf[i-1] != '/')
                i--;
            while (i > 0 && buf[i-1] == '/')
                i--;
        }
        else
        {
            buf[i++] = '/';
            while (f[0] != '\0' && f[0] != '/')
                buf[i++] = *f++;
            while (f[0] == '/')
                f++;
        }
    }
    buf[i] = '\0';
    if (i > MAX_FILENAME_LEN)
    {
        fprintf(fout, "add_cd_path reached lengt %d\n", i);
        exit(1);
    }
    //fprintf(fout, "add_cd_path %s %s => %s\n", cd_path, filename, buf);
    strcpy(filename, buf);
    //fprintf(log_file, "%s\n", filename);
}

void read_filename(char *filename, char *&s)
{
    if (!parse_filename(filename, s))
    {
        fprintf(fout, "Failed to parse filename from '%s'\n", s);
        exit(0);
    }
    add_cd_path(filename);
}


        
bool process_trace_file()
{
    FILE *f = fopen("trace.txt", "r");
    
    FILE *fout_usage = 0;
    
    char buffer[10000];

    long line_nr = 0;
    
    while (fgets(buffer, 9999, f))
    {
        // Skip the first 8 lines
        line_nr++;
        if (line_nr <= 8) continue;
        
        int len = strlen(buffer);
        if (len > 0 && buffer[len-1] != '\n')
        {
            printf("Line '%s' does not end with newline\n", buffer);
            return false;
        }
        //fprintf(fout, "Line: %s", buffer);
        
        char filename[MAX_FILENAME_LEN+1];
        
        char *s = buffer;
        unsigned long pid = read_unsigned_long(s);
        Process *process = line_nr == 9 ? next_process(pid) : find_process(pid);
        
        while (*s == ' ' || *s == '\t')
            s++;
        //fprintf(fout, "%lu: %s", pid, s);
        
        if (accept_string("execve(", s))
        {
            read_filename(filename, s);
            File *exec_file = get_file(filename);
            Action *action = new Action(exec_file, process, 'e');
            if (strcmp(filename, "/usr/bin/tcc-boot0") == 0)
            {
                fprintf(fout, "Stop at %lu: %s\n", pid, s);
                break;
            }
            exec_file->init_source();
        }
        else if (accept_string("open(", s))
        {
            read_filename(filename, s);
            //fprintf(fout, "open %s", s);
            bool o_rdonly = false;
            bool o_wronly = false;
            bool o_rdwr = false;
            bool o_creat = false;
            bool o_trunc = false;
            bool o_excl = false;
            if (!accept_string(", ", s))
                fprintf(fout, "Expecting ', at '%s'\n", s);
            for (;*s != '\0'; s++)
            {
                if (accept_string("O_RDONLY", s))
                    o_rdonly = true;
                else if (accept_string("O_WRONLY", s))
                    o_wronly = true;
                else if (accept_string("O_RDWR", s))
                    o_rdwr = true;
                else if (accept_string("O_CREAT", s))
                    o_creat = true;
                else if (accept_string("O_TRUNC", s))
                    o_trunc = true;
                else if (accept_string("O_EXCL", s))
                    o_excl = true;
                else
                {
                    fprintf(fout, "Unknown %s", s);
                    break;
                }
                if (*s == ',')
                    break;
                if (*s != '|')
                    break;
            }
            unsigned long mode = 0;
            if (accept_string(", ", s))
            {
                mode = read_unsigned_long(s);
            }
            if (!accept_string(") = ", s))
            {
                fprintf(fout, "Expecting ') = ' at '%s'\n", s);
                return false;
            }
            long handle = read_long(s);
            //if (*s != '\n')
            //  fprintf(fout, "open end with '%s'\n", s);
            if ((o_rdonly ? 1 : 0) + (o_wronly ? 1 : 0) + (o_rdwr ? 1 : 0) != 1)
                fprintf(fout, "Warning: Open '%s' as undefined read/write mode\n", filename);

            if (handle > -1)
            {
                File *file = get_file(filename);
                Action *action = new Action(file, process, 'o');
                action->file_handle = handle;
                action->o_rdonly = o_rdonly;
                action->o_wronly = o_wronly;
                action->o_rdwr = o_rdwr;
                action->o_creat = o_creat;
                action->o_trunc = o_trunc;
                action->o_excl = o_excl;
                if (o_creat)
                    action->mode = mode;
                file->init_source();
            }
        }
        else if (accept_string("close(", s))
        {
            unsigned long handle = read_unsigned_long(s);
            if (*s != ')')
                fprintf(fout, "Expecting ')' at '%s'\n", s);
            Action *last_open_action = process->lastOpenAction(handle);
            if (last_open_action == 0)
                fprintf(fout, "Error: Handle %ld not opened by process %d\n", handle, process->nr);
            else if (last_open_action->is_closed)
                fprintf(fout, "Warning: File %s already closed for process %d\n", last_open_action->file->name, process->nr);
            else
                last_open_action->is_closed = true; 
        }
        else if (accept_string("chmod(", s))
        {
            read_filename(filename, s);
            unsigned long mode = 0;
            if (accept_string(", ", s))
            {
                mode = read_unsigned_long(s);
            }
            if (*s != ')')
            {
                fprintf(fout, "Expecting ')' at '%s'\n", s);
                return false;
            }
            File *file = get_file(filename);
            Action *action = new Action(file, process, 'c');
            action->mode = mode;
        }
        else if (accept_string("chdir(", s))
        {
            read_filename(filename, s);
            if (!accept_string(") = ", s))
            {
                fprintf(fout, "Expecting ') = ' at '%s'\n", s);
                return false;
            }
            
            long result = read_long(s);
            //if (!accept_string("-1 ENOENT (No such file or directory)", s))
            //  result = read_unsigned_long(s);
            if (result == 0)
                strcpy(cd_path, filename);
        }
        else if (accept_string("unlink(", s))
        {
            read_filename(filename, s);
            if (*s != ')')
                fprintf(fout, "Expecting ')' at '%s'\n", s);
            File *file = get_file(filename);
            new Action(file, process, 'r');
        }
        else if (accept_string("fork(", s))
        {
            if (!accept_string(") = ", s))
            {
                fprintf(fout, "Expecting ') = ' at '%s'\n", s);
                return false;
            }
            long new_pid = read_unsigned_long(s);
            if (*s != '\n')
                fprintf(fout, "fork end with '%s'\n", s);
            Process *new_process = next_process(new_pid);
            //fprintf(fout, "fork created %lu %lu\n", new_pid, new_process->pid);
            new_process->parent = process;
            Action *action = new Action(0, process, 'E');
            action->child_process = new_process;
        }
        else if (accept_string("+++ exited with ", s))
        {
        }
        else if (accept_string("--- SIGCHLD ", s))
        {
        }
        else
        {
            fprintf(fout, "Unknown: '%s'\n", buffer);
            break;
        }
    }
    fclose(f);
    
    return true;
}
    
// ----------------------------------------------------

class Source
{
public:
    const char *url;
    Source *next;
    
    Source(const char *u, Source *n) : url(u), next(n) {}
};


void collect_sources(Process *process, Source **ref_sources)
{
    //indent(fout); fprintf(fout, "Process %d\n", process->nr);
    //indent_depth += 4;
    for (Action *action = process->actions; action != 0; action = action->next_in_process)
    {
        if (action->kind == 'o' && (action->o_rdonly || (action->o_rdwr && !action->o_trunc)))
        {
            File *file = action->file;      
            if (file->url != 0)
            {
                const char *url = file->url;
                //indent(fout); fprintf(fout, "Found %s\n", url);
                Source **ref_source = ref_sources;
                while (*ref_source != 0 && strcmp((*ref_source)->url, url) < 0)
                    ref_source = &(*ref_source)->next;
                if (*ref_source == 0 || strcmp((*ref_source)->url, url) > 0)
                    *ref_source = new Source(url, *ref_source);
            }
            else
            {
                Process *produced_by = 0;
                for (Action *file_action = file->actions; file_action != 0; file_action = file_action->next_on_file)
                    if (file_action->process == process)
                        break;
                    else if (file_action->kind == 'r')
                        produced_by = 0;
                    else if (file_action->is_produced())
                        produced_by = file_action->process;
                if (produced_by != 0)
                    collect_sources(produced_by, ref_sources);
            }
        }
    }
    //indent_depth -= 4;
}



bool include_source = false;

void output_file(FILE *f, FILE *f_source, bool binary)
{
    if (f_source == 0) return;
    
    fprintf(f, "<PRE>");
    if (binary)
    {
        int i = 0;
        unsigned char ch = fgetc(f_source);
        while (!feof(f_source))
        {
            fprintf(f, " %02X", ch);
            if (++i % 10 == 0)
                fprintf(f, "\n");
            ch = fgetc(f_source);
        }
    }
    else
    {
        char ch = fgetc(f_source);
        if (ch != -1)
        {
            int col = 0;
            while (!feof(f_source))
            {
                col++;
                if (ch == '<')
                    fprintf(f, "&lt;");
                else if (ch == '>')
                    fprintf(f, "&gt;");
                else if (ch == '&')
                    fprintf(f, "&amp;");
                else if ((unsigned char)ch == 160)
                    fprintf(f, "&nbsp;");
                else if ((unsigned char)ch == 169)
                    fprintf(f, "&copy;");
                else if ((unsigned char)ch == 194)
                    fprintf(f, "&Acirc;");
                else if ((unsigned char)ch == 195)
                    fprintf(f, "&Atilde;");
                else if ((unsigned char)ch == 197)
                    fprintf(f, "&Aring;");
                else if ((unsigned char)ch == 216)
                    fprintf(f, "&Oslash;");
                else if ((unsigned char)ch == 231)
                    fprintf(f, "&ccedil;");
                else if ((unsigned char)ch == 246)
                    fprintf(f, "&ouml;");
                else if (ch < 0)
                    fprintf(f, "&#%d;", (unsigned char)ch);
                else if (ch == '\n' || ch == 12)
                {
                    fprintf(f, "\n");
                    col = 0;
                }
                else if (ch == '\t')
                {
                    fprintf(f, " ");
                    while (col % 4 > 0)
                    {
                        fprintf(f, " ");
                        col++;
                    }
                }
                else if (ch < ' ')
                    ; // skip control characters
                else
                    fprintf(f, "%c", ch);
                ch = fgetc(f_source);
            }
        }
    }
    
    fprintf(f, "</PRE>");
    fclose(f_source);
}



void write_html_file(FILE *f, File *file, bool binary)
{
    fprintf(f, "<H3><A NAME=\"F%d\">File %s</A></H3>\n\n<UL>\n", file->nr, file->name);
    
    for (Action *action = file->actions; action != 0; action = action->next_on_file)
    {
        if (action->kind == 'r')
            break;
        if (action->kind == 'e')
            fprintf(f, "<LI>Executed in <A HREF=\"#S%d\">Process %d</A>\n", action->process->nr, action->process->nr);
        if (action->kind == 'o')
        {
            if (action->o_wronly || action->o_rdwr)
                break;
            if (action->o_rdonly)
                fprintf(f, "<LI>Input for <A HREF=\"#S%d\">Process %d</A>\n", action->process->nr, action->process->nr);
        }
    }
    fprintf(f, "</UL>\n\n");
    
    FILE *f_source = fopen(file->source_name, "r");

    if (f_source == 0)
    {
        fprintf(f, "(Source not found at '%s')\n", file->source_name);
        return;
    }

    if (strncmp(file->source_name, source_dir, len_source_dir) == 0)
        fprintf(f, "Live-bootstrap source file is '%s'.<BR>\n", file->source_name + len_source_dir);
    else
        fprintf(f, "Source file is '%s'.<BR>\n", file->source_name);
    if (file->url != 0)
    {
        fprintf(f, "URL: <A HREF=\"%s\">%s</A>\n", file->url, file->url);
        //fprintf(fout, "Source: %s, URL: %s\n", file->source_name, file->url);
    }
    else
        fprintf(f, "<B>No URL</B>\n");
    
    size_t len = strlen(file->source_name);
    
    if (   (len > 7 && strcmp(file->source_name + len - 7, ".tar.gz") == 0)
        || (len > 8 && strcmp(file->source_name + len - 8, ".tar.bz2") == 0))
    {
        fprintf(f, "(Not shown)\n");
        fclose(f_source);
        return;
    }
    
    output_file(f, f_source, binary);
}


void write_html(FILE *f)
{
    fprintf(f, 
        "<HTML><HEAD>\n<TITLE>live-bootstrap</TITLE>\n"
        "</HEAD><BODY>\n\n<H1>live-bootstrap</H1>"
        "<!--ONEWAY-->\n"
        "This page is produced by the version of the program <TT>scan_trace.cpp</TT>\n"
        "listed at <A HREF=\"#Parser\">the bottom</A> of this page.\n"
        "The program parsers the contents of <TT>trace.txt</TT> file that is produced by\n"
        "running the <TT>run_chroot</TT> Bash script from a sibling directory of a clone of\n"
        "<A HREF=\"https://github.com/fosslinux/live-bootstrap\">fosslinux/live-bootstrap</A>\n"
        "(the commit <A HREF=\"https://github.com/fosslinux/live-bootstrap/commit/%s\"><TT>%.8s</TT></A>)\n"
        "in which the <A HREF=\"https://github.com/fosslinux/live-bootstrap/blob/%s/download-distfiles.sh\"\n"
        "><TT>download-distfiles.sh</TT></A> script has been executed as well.\n"
        "(This is still work in progress.)\n"
        "<P>\n"
        "The code displayed on this page is not copyrighted by me but by the owners of\n"
        "respective repositories as also mentioned in the headers of the various files.\n"
        "<UL>\n"
        "<LI><A HREF=\"#Seeds\">Binary seeds files</A>\n"
        "<LI><A HREF=\"#Processes\">Processes</A>\n"
        "<LI><A HREF=\"#Input\">Input source files</A>\n"
        "<LI><A HREF=\"#Output\">Output files</A>\n"
        "<LI><A HREF=\"#Parser\">Parse program</A>\n"
        "</UL>\n", live_bootstrap_commit, live_bootstrap_commit, live_bootstrap_commit);

    fprintf(f, "\n\n<H2><A NAME=\"Seeds\">Binary seeds files</A></H2>\n\n");
    
    for (File *file = files; file != 0; file = file->next)
        if (file->exec_before_created())
            write_html_file(f, file, true);
     
    fprintf(f, "\n<H2><A NAME=\"Processes\">Processes</A></H2>\n\n");
    for (Process *process = all_processes; process != 0; process = process->next)
    {
        fprintf(f, "<H3><A NAME=\"S%d\">Process %d</A></H3>\n\n", process->nr, process->nr);
        if (process->parent != 0)
            fprintf(f, "(Executed by <A HREF=\"#S%d\">Process %d</A>)\n", process->parent->nr, process->parent->nr);
        fprintf(f, "<UL>\n");
        for (Action *action = process->actions; action != 0; action = action->next_in_process)
        {
            if (action->kind == 'E' && action->child_process != 0)
            {
                fprintf(f, "<LI>Executes <A HREF=\"#S%d\">Process %d</A>\n", action->child_process->nr, action->child_process->nr);
            }
            else
            {
                const char *oper = action->oper_name();
                if (oper != 0)
                {
                    bool repeated = false;
                    for (Action *prev_action = process->actions; prev_action != action; prev_action = prev_action->next_in_process)
                        if (prev_action->kind == 'o' && prev_action->file == action->file && prev_action->oper_name() == oper)
                        {
                            repeated = true;
                            break;
                        }
                    
                    if (!repeated)
                    {
                        File *file = action->file;
                        fprintf(f, "<LI>%s ", oper);
                        
                        if (action->is_produced())
                        {
                            fprintf(f, "%s\n<UL>\n", file->name);
                            for (Action *file_action = action->next_on_file; file_action != 0; file_action = file_action->next_on_file)
                                if (file_action->kind == 'r')
                                {
                                    fprintf(f, "<LI>Deleted by <A HREF=\"#S%d\">process %d</A>\n", file_action->process->nr, file_action->process->nr);
                                    break;
                                }
                                else if (file_action->kind == 'o' && (file_action->o_creat || ((file_action->o_wronly || file_action->o_rdwr) && file_action->o_trunc)))
                                    break;
                                else if (file_action->kind == 'e' || file_action->o_rdonly)
                                    fprintf(f, "<LI>%s <A HREF=\"#S%d\">process %d</A>\n",
                                        file_action->kind == 'e' ? "Used as executable" : 
                                        file_action->kind == 'o' ? (file_action->o_rdonly ? "Used as input" : file_action->o_wronly ? "Produced by" : file_action->o_rdwr ? "Modified by" : "Modified by") :
                                        "Used in",
                                        file_action->process->nr, file_action->process->nr);
                            fprintf(f, "</UL>\n\n");
                        }
                        else
                        {
                            Process *produced_by = 0;
                            for (Action *file_action = file->actions; file_action != 0; file_action = file_action->next_on_file)
                                if (file_action->process == process)
                                    break;
                                else if (file_action->kind == 'r')
                                    produced_by = 0;
                                else if (file_action->is_produced())
                                    produced_by = file_action->process;
                            if (file->is_source)
                                fprintf(f, "<A HREF=\"#F%d\">%s</A>", file->nr, file->name);
                            else
                                fprintf(f, "%s", action->file->name);
                            File *file_copy_from = file;
                            while (file_copy_from->copy_from != 0)
                                file_copy_from = file_copy_from->copy_from;
                            if (file_copy_from->url != 0)
                            {
                                fprintf(f, " from <A HREF=\"%s\">source</A>", file_copy_from->url);
                                if (file != file_copy_from)
                                    fprintf(f, " (through copy)");
                                if (produced_by)
                                    fprintf(f, " (produced by <A HREF=\"#S%d\">process %d</A>)", produced_by->nr, produced_by->nr);
                            }
                            else if (produced_by != 0)
                                fprintf(f, " produced by <A HREF=\"#S%d\">process %d</A>", produced_by->nr, produced_by->nr);
                            fprintf(f, "\n");
                        }
                    }
                }
            }
        }
        fprintf(f, "</UL>\n\n");
        
        if (process->nr == 731)
        {
            Source *sources = 0;
            //fprintf(fout, "Process %d\n", process->nr);
            collect_sources(process, &sources);
            
            fprintf(f, "<P>Sources used:\n<UL>\n");
            for (Source *source = sources; source != 0; source = source->next)
                fprintf(f, "<LI> %s\n", source->url);
            fprintf(f, "</UL>\n");
        }
    }
        
    fprintf(f, "<H2><A NAME=\"Input\">Input source files</A></H2>\n\n");
    
    for (File *file = files; file != 0; file = file->next)
        if (file->used_as_input()) //(file->is_source && !file->exec_before_created())
            write_html_file(f, file, false); 

    fprintf(f, "\n<H2><A NAME=\"Output\">Output files</A></H2>\n\n\n");
 
    for (int t = 0; t < 3; t++)
    {
        switch (t)
        {
            case 0: fprintf(f, "Executables files:\n<UL>\n"); break;
            case 1: fprintf(f, "Intermediary files (not from sources and used):\n<UL>\n"); break;
            case 2: fprintf(f, "Produced (not from source and also not used):\n<UL>\n"); break;
        }
        for (File *file = files; file != 0; file = file->next)
            if (!file->used_as_input() && !file->exec_before_created())
            {
                bool used = false;
                bool executed = false;
                unsigned long mode = 0;
                int process_nr = -1;
                for (Action *action = file->actions; action != 0; action = action->next_on_file)
                {
                    if (action->kind == 'e')
                        executed = true;
                    else if (action->kind == 'o')
                    {
                        if (action->o_creat || action->o_wronly)
                        {
                            mode = action->mode;
                            process_nr = action->process->nr;
                        }
                        else if (action->o_rdonly || (action->o_rdwr && !action->o_trunc))
                            used = true;
                    }
                    else if (action->kind == 'r')
                    {
                        used = false;
                        executed = false;
                        process_nr = -1;
                    }
                    else if (action->kind == 'c')
                    {
                        mode = action->mode;
                    }
                }
                if (process_nr != -1)
                {
                    bool is_executable = (mode & 0700) == 0700;
                    if (   (t == 0 && is_executable)
                        || (t == 1 && file->url == 0 && !is_executable && used)
                        || (t == 2 && file->url == 0 && !is_executable && !used))
                    {
                        fprintf(f, "<LI> %s", file->name);
                        if (process_nr > 0)
                            fprintf(f, " produced by <A HREF=\"#S%d\">Process %d</A>", process_nr, process_nr);
                        if (mode != 0 && mode != 0600 && mode != 0700)
                            fprintf(f, " (mode is %lo)", mode);
                        if (executed)
                            fprintf(f, " (also executed)");
                        fprintf(f, "\n");
                    }
                }
            }
        fprintf(f, "</UL>\n\n");
    }

    fprintf(f, "\n<H2><A NAME=\"Parser\">Parse program</A></H2>\n\n");
    fprintf(f, "Below the Bash script <TT>run_chroot</TT> to produce the <TT>trace.txt</TT> file.\n<P>\n");
    output_file(f, fopen("run_chroot", "r"), false);
    fprintf(f, "Below the version of the <TT>scan_trace.cpp</TT> program is given that is used to produce this page.\n<P>\n");
    output_file(f, fopen("scan_trace.cpp", "r"), false);
    
    fprintf(f,
        "\n\n"
        "<P><HR>\n"
        "<ADDRESS>\n"
        "<A HREF=\"index.html\">Home</A>\n"
        "</ADDRESS>\n"
        "</BODY></HTML>\n");
}

int main(int argc, char *argv[])
{
    len_source_dir = strlen(source_dir);

    init_subModules();
    
    if (!process_trace_file())
        return 0;
    
    for (Process *process = all_processes; process != 0; process = process->next)
    {
        Action *action = process->actions;
        if (   action != 0 && action->kind == 'e'
            && action->file != 0 && strcmp(action->file->name, "/usr/bin/cp") == 0)
        {
            action = action->next_in_process;
            if (action != 0 && action->kind == 'o' && action->o_rdonly)
            {
                File *source = action->file;
                action = action->next_in_process;
                if (action != 0 && action->kind == 'o' && action->o_wronly)
                {
                    fprintf(fout, "Copy %s -> %s\n", source->name, action->file->name);
                    action->file->copy_from = source;
                }
            }
        }
    }
        

    
    SubModule *subModules = 0;
    
    FILE *f_html = fopen("docs/index.html", "w");
    if (f_html != 0)
    {
        write_html(f_html);
        fclose(f_html);
    }
    
    return 0;
}


Home