• some old helper tools for rotation

    From Eli the Bearded@3:633/10 to All on Wed May 20 17:25:09 2026
    A few weeks ago I found these in an old backup of mine. There's four
    utilies, three of them using a compiled C heler tool and the fourth
    based on awk. Combined they provide a way to rotate ASCII art 90
    degrees, 180 degrees, 270 degrees, and to left-right flip an image.

    The left-right flip is by Jef Poskanzer ( www.acme.com ) and clearly
    licensed. The others are of ambitious origin and have no clear license.
    I suspect the authors would be 100% okay with my sharing this old code
    for the purpose of hobbyists to use non-commercially.

    Code distributed in the old-school shell archive format. You can extract
    by hand easily enough or use `sh` to do it automatically. There was no
    makefile when I got this, but "make asciitool" worked for me. My only
    changes to the code were to add the #include lines above the comment
    in the C file and add line breaks to the C file usage message.

    Elijah
    ------
    # This is a shell archive. Save it in a file, remove anything before
    # this line, and then unpack it by entering "sh file". Note, it may
    # create directories; files and directories will be owned by you and
    # have default permissions.
    #
    # This archive contains:
    #
    # notes
    # asciitool.c
    # ccw90
    # cw90
    # tb
    # lr
    #
    echo x - notes
    sed 's/^X//' >notes << 'END-of-notes'
    XDownloaded from
    X gopher://samuell.com:70/
    Xsometime in June 2006
    END-of-notes
    echo x - asciitool.c
    sed 's/^X//' >asciitool.c << 'END-of-asciitool.c'
    X#include <stdlib.h>
    X#include <stdio.h>
    X/* **********************************************************************
    X asciitool.c An ASCII Rotation and Mirroring tool
    X
    Xcompile with: cc -o asciitool asciitool.c
    X
    XNot my code! (Joshua Bell, jsbell@acs.ucalgary.ca)
    X
    XWell, some of it is, but not much.
    XChanges: - changed if/else if/else if/else if... to a switch
    X - fixed bug with -r270 (no longer requires rectangular images)
    X - made Infile and Outfile default to STDIN and STDOUT (for pipes) X - made incorrect calling syntax quit program
    X - reformatted the source to be more concise
    X
    Xasciitool -{mh|mv|r90|r180|r270} [Infile [Outfile]]
    X -mh Mirror horizontally
    X -mv Mirror vertically
    X -r90 Rotate 90 degrees counterclockwise
    X -r180 Rotate 90 degrees counterclockwise
    X -r270 Rotate 90 degrees counterclockwise
    X
    X********************************************************************** */
    X
    X#include <stdio.h>
    X#include <string.h>
    X
    X#define NUM_FLAGS 5
    X
    Xchar *FlagStrings[NUM_FLAGS] = { "-mv", "-mh", "-r90", "-r180", "-r270" }; Xenum { V_MIRROR, H_MIRROR, R90, R180, R270 };
    X
    Xchar **GetFile(FilePtr, NumberOfLinesPtr, ErrorString)
    X FILE *FilePtr;
    X int *NumberOfLinesPtr;
    X char *ErrorString;
    X{
    X char **FileLines;
    X char Buffer[255];
    X int CurrentLine, c, Error;
    X
    X ErrorString[0] = '\0';
    X
    X CurrentLine = 0;
    X Error = 0;
    X
    X while((c = fgetc(FilePtr)) != EOF) {
    X ungetc(c, FilePtr);
    X fgets(Buffer, sizeof(Buffer), FilePtr);
    X
    X if(CurrentLine == 0) {
    X if((FileLines = (char **)malloc(sizeof(char *))) == NULL) {
    X strcpy(ErrorString, "Memory Allocation Error: <malloc>");
    X return(NULL);
    X }
    X
    X } else {
    X if((FileLines =
    X (char **)realloc(FileLines,(1 + CurrentLine) * sizeof(char *)))
    X == NULL) {
    X strcpy(ErrorString, "Memory Allocation Error: <realloc>\n");
    X return(NULL);
    X }
    X }
    X
    X if((FileLines[CurrentLine] = (char *)malloc(strlen(Buffer) + 1)) == NULL) {
    X strcpy(ErrorString, "Memory Allocation Error: <malloc>\n");
    X return(NULL);
    X }
    X
    X strcpy(FileLines[CurrentLine++], Buffer);
    X }
    X
    X *NumberOfLinesPtr = CurrentLine;
    X
    X return(FileLines);
    X
    X} /* GetFile */
    X
    Xint main(argc, argv)
    X int argc;
    X char **argv;
    X{
    X int i, j, k, Flag, NumberOfLines, Length, MaxLength;
    X FILE *Infile, *Outfile;
    X char String[255];
    X char **FileLines, **NewFileLines, *CharPtr;
    X
    X if((argc < 2) | (argc > 4)) {
    X printf("Usage: %s -{mh|mv|r90|r180|r270} [Infile [Outfile]]\n", argv[0]); X printf(" -mh Mirror horizontally\n");
    X printf(" -mv Mirror vertically\n");
    X printf(" -r90 Rotate 90 degrees counterclockwise\n");
    X printf(" -r180 Rotate 90 degrees counterclockwise\n");
    X printf(" -r270 Rotate 90 degrees counterclockwise\n");
    X return(-1);
    X }
    X
    X switch (argc) {
    X case 2:
    X Infile = stdin;
    X Outfile = stdout;
    X break;
    X case 3:
    X if((Infile = fopen(argv[2], "r")) == NULL)
    X {
    X printf("Could not open input file: %s\n", argv[1]);
    X return(-1);
    X }
    X Outfile = stdout;
    X break;
    X case 4:
    X if((Outfile = fopen(argv[3], "w")) == NULL)
    X {
    X printf("Could not open output file: %s\n", argv[2]);
    X return(-1);
    X }
    X break;
    X }
    X
    X
    X strcpy(String, argv[1]);
    X
    X for(i = 0; String[i]; i ++)
    X if(String[i] >= 'A' && String[i] <= 'Z')
    X String[i] += ('a' - 'A');
    X
    X for(i = 0; i < NUM_FLAGS; i ++)
    X if(!strcmp(String, FlagStrings[i]))
    X break;
    X
    X if(i == NUM_FLAGS)
    X {
    X printf("Illegal flag: %s\n", argv[3]);
    X return(-1);
    X }
    X else
    X Flag = i;
    X
    X if((FileLines = GetFile(Infile, &NumberOfLines, String)) == NULL) {
    X printf("%s\n", String);
    X return(-1);
    X }
    X
    X for(i = 0; i < NumberOfLines; i ++) {
    X if((CharPtr = strrchr(FileLines[i], '\n')) != NULL)
    X *CharPtr = '\0';
    X
    X Length = strlen(FileLines[i]);
    X
    X if(i == 0 || (Length > MaxLength))
    X MaxLength = Length;
    X }
    X
    X switch (Flag) {
    X
    X case V_MIRROR:
    X for(i = NumberOfLines - 1; i >= 0; i --)
    X fprintf(Outfile, "%s\n", FileLines[i]);
    X break;
    X
    X case H_MIRROR:
    X for(i = 0; i < NumberOfLines; i ++) {
    X Length = strlen(FileLines[i]);
    X
    X for(j = 0; j < Length; j ++)
    X String[Length - j - 1] = FileLines[i][j];
    X
    X String[j] = '\0';
    X fprintf(Outfile, "%*s%s\n", MaxLength - Length, "", String);
    X }
    X break;
    X
    X case R180:
    X for(i = NumberOfLines - 1; i >= 0; i --) {
    X Length = strlen(FileLines[i]);
    X
    X for(j = 0; j < Length; j ++)
    X String[Length - j - 1] = FileLines[i][j];
    X
    X String[j] = '\0';
    X fprintf(Outfile, "%*s%s\n", MaxLength - Length, "", String);
    X }
    X break;
    X
    X/*** For "real" rotations, need extra code ***/
    X
    X default:
    X
    X if((NewFileLines =
    X (char **)malloc(MaxLength * sizeof(char *))) == NULL) {
    X printf("Memory Allocation Error: <malloc>");
    X return(-1);
    X }
    X
    X for(i = 0; i < MaxLength; i ++) {
    X if((NewFileLines[i] =
    X (char *)malloc((NumberOfLines + 1) * sizeof(char))) == NULL) {
    X printf("Memory Allocation Error: <malloc>");
    X return(-1);
    X }
    X }
    X
    X switch (Flag) {
    X
    X case R90:
    X
    X for(i = 0; i < NumberOfLines; i ++)
    X {
    X for(j = 0; FileLines[i][j]; j ++)
    X NewFileLines[MaxLength - 1 - j][i] = FileLines[i][j];
    X
    X while(j < MaxLength) {
    X NewFileLines[MaxLength - 1 - j][i] = ' ';
    X j++;
    X }
    X }
    X break;
    X
    X case R270:
    X
    X for(i = 0; i < NumberOfLines; i ++) {
    X Length = strlen(FileLines[i]);
    X
    X for(k = 0, j = MaxLength - 1; j >= 0; j --, k ++)
    X if (j >= Length)
    X NewFileLines[MaxLength - k - 1][NumberOfLines - i - 1] = ' ';
    X else
    X NewFileLines[MaxLength - k - 1][NumberOfLines - i - 1] =
    X FileLines[i][j];
    X
    X while(k < MaxLength) {
    X NewFileLines[MaxLength - k - 1][NumberOfLines - i - 1] = ' ';
    X k++;
    X }
    X }
    X
    X break;
    X }
    X
    X for(j = 0; j < MaxLength; j ++) {
    X NewFileLines[j][NumberOfLines] = '\0';
    X fprintf(Outfile, "%s\n", NewFileLines[j]);
    X }
    X }
    X
    X
    X for(i = 0; i < NumberOfLines; i ++)
    X free((void *)FileLines[i]);
    X
    X free((void *)FileLines);
    X
    X fclose(Infile), fclose(Outfile);
    X
    X return(0);
    X
    X} /* main */
    END-of-asciitool.c
    echo x - ccw90
    sed 's/^X//' >ccw90 << 'END-of-ccw90'
    X#!/bin/sh
    X#
    X# ccw90 - rotates ASCII pictures by 90 degrees counter clockwise
    X#
    X# by Joshua Bell <jsbell@acs.ucalgary.ca>, July 1994
    X#
    X# Requires the asciitool program is present in your search path.
    X#
    X# Inspiration from Jef Poskanzer's "lr" utility for left/right flips.
    X#
    X# Mapping is: _-~^<>v/\NZ!]|[()nu:;{}"'`,CUciIl17t=H
    X# to: |||<v^>\/ZN-~-_un()""_~:\/\U)u----r+H=
    X
    Xcat $* |
    X ./asciitool -r90 |
    X tr '_\-~^<>v/\\NZ!]|[()nu:;{}"'\''`,CUciIl17t=H' \
    X '|||<v^>\\/ZN\-~\-_un()""_~:\\/\\U)u\-\-\-\-r+H=' |
    X sed 's/ *$//'
    X
    END-of-ccw90
    echo x - cw90
    sed 's/^X//' >cw90 << 'END-of-cw90'
    X#!/bin/sh
    X#
    X# cw90 - rotates ASCII pictures by 90 degrees clockwise
    X#
    X# by Joshua Bell <jsbell@acs.ucalgary.ca>, July 1994
    X#
    X# Requires the asciitool program is present in your search path.
    X#
    X# Inspiration from Jef Poskanzer's "lr" utility for left/right flips.
    X#
    X# Mapping is: _-~^<>v/\NZ!]|[()nu:;{}"'`,CUciIl1rt=H
    X# to: |||>^v<\/ZN-_-~nu)(""~_:\/\nCn----7+H=
    X
    Xcat $* |
    X ./asciitool -r270 |
    X tr '_\-~^<>v/\\NZ!]|[()nu:;{}"'\''`,CUciIl1rt=H' \
    X '|||>^v<\\/ZN\-_\-~nu)(""~_:\\/\\nCn\-\-\-\-7+H=' |
    X sed 's/ *$//'
    X
    END-of-cw90
    echo x - tb
    sed 's/^X//' >tb << 'END-of-tb'
    X#!/bin/sh
    X#
    X# tb - flips ASCII pictures top for bottom
    X#
    X# by Joshua Bell <jsbell@acs.ucalgary.ca>, Feb 1994
    X#
    X# Requires the asciitool program is present in your search path.
    X#
    X# Inspiration from Jef Poskanzer's "lr" utility for left/right flips.
    X#
    X# Mapping is: ;!~_`,.'"^/\AFJLMPSTUVWYZbfghimnpqrstuvwyz257
    X# to: :i_~,`'..v\/VL7TWb2LAAMASPt6y!wubdtzfn^mhsSZJ
    X
    Xcat $* |
    X ./asciitool -mv |
    X tr ';!~_\140,.\047"^/\\AFJLMPSTUVWYZbfghimnpqrstuvwyz257' \
    X ':i_~,\140\047..v\\/VL7TWb2LAAMASPt6y!wubdtzfn^mhsSZJ' |
    X sed 's/ *$//'
    X
    END-of-tb
    echo x - lr
    sed 's/^X//' >lr << 'END-of-lr'
    X#!/bin/sh
    X#
    X# lr - reverse ASCII pictures left for right
    X#
    X# Copyright (C) 1994 by Jef Poskanzer.
    X#
    X# Permission to use, copy, modify, and distribute this software and its
    X# documentation for any purpose and without fee is hereby granted, provided
    X# that the above copyright notice appear in all copies and that both that
    X# copyright notice and this permission notice appear in supporting
    X# documentation. This software is provided "as is" without express or
    X# implied warranty.
    X
    X# Translation of . -> , changed removed. Better to lose some symmetry than
    X# induce asymmetry, IMHO. (jsbell@acs)
    X
    Xcat $* |
    X expand |
    X awk '{printf "%s%s\n", $0, substr(" ", length)}' |
    X rev |
    X tr '()<>{}\[]/\\.,\140\047LJZSE3dbqp' ')(><}{]\[\\/..\047\140JLSZ3Ebdpq' |
    X sed 's/ *$//'
    END-of-lr
    exit



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Daniel@3:633/10 to All on Fri May 22 16:43:11 2026
    Eli the Bearded <*@eli.users.panix.com> writes:

    A few weeks ago I found these in an old backup of mine. There's four
    utilies, three of them using a compiled C heler tool and the fourth
    based on awk. Combined they provide a way to rotate ASCII art 90
    degrees, 180 degrees, 270 degrees, and to left-right flip an image.

    The left-right flip is by Jef Poskanzer ( www.acme.com ) and clearly licensed. The others are of ambitious origin and have no clear license.
    I suspect the authors would be 100% okay with my sharing this old code
    for the purpose of hobbyists to use non-commercially.

    Code distributed in the old-school shell archive format. You can extract
    by hand easily enough or use `sh` to do it automatically. There was no makefile when I got this, but "make asciitool" worked for me. My only
    changes to the code were to add the #include lines above the comment
    in the C file and add line breaks to the C file usage message.

    Wow great find. Thanks for the post.

    --
    Daniel
    sysop | air & wave bbs
    finger | info@bbs.airandwave.net

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)