kor

best way to open multiple file handles?

24 posts in this topic

#include <File.au3>
#include <FileConstants.au3>
Global $hLog
_Log("testing1", "HR") ; file 1
_Log("testing2", "HR") ; file 1
_Log("testing3", "Payroll") ; file 2
_Log("testing4") ; no prefix file 3

Func _Log($sText, $sAppend = "")
    ; Writes script output to console, file, and command window if compiled
    Local $sPrefix, $sLogName = @MON & "-" & @MDAY & "-" & @YEAR & "--" & @HOUR & "-" & @MIN
    If $sAppend <> "" Then $sLogName = $sAppend & "-" & $sLogName ; prefix log ID when writing to multiple log files in a single script
    If Not $hLog Then
        $hLog = FileOpen(@ScriptDir & "\logs\" & $sLogName & ".log", $FO_CREATEPATH + $FO_OVERWRITE) ; open log file
        If $hLog = -1 Then ; confirm RW access for log file
            ConsoleWrite("! [ERROR] - Can't create log file" & @CR)
            Exit(1)
        EndIf
    EndIf
    Switch $sText
        Case StringInStr($sText, "ERROR") > 0
            $sPrefix = "!" ; red
        Case StringInStr($sText, "WARN") > 0
            $sPrefix = "-" ; orange
        Case StringInStr($sText, "ACTION") > 0
            $sPrefix = "+" ; green
        Case Else
            $sPrefix = ">" ; blue
    EndSwitch
    ConsoleWrite($sPrefix & " " & $sText & @CRLF) ; write to console and commmand window (if compiled)
    _FileWriteLog($hLog, $sText) ; write to log file
EndFunc   ;==>_Log

what would you suggest to be able to open multiple file handles within a single script based on the $sAppend value?

Share this post


Link to post
Share on other sites



I assume you mean keeping track of them.

Use a 2d array, where [n][0] is the file name and [n][1] is the file handle.

I switched to using file names directly rather than handles.

#include <File.au3>

_Log("testing", "HR")

Func _Log($sText, $sPrefix = "")
    Local $sColor, $sLogPath = @ScriptDir & "\logs\", $sLogName = @MON & "-" & @MDAY & "-" & @YEAR & "--" & @HOUR & "-" & @MIN & ".log"
    If $sPrefix <> "" Then $sLogName = $sPrefix & "-" & $sLogName ; prefix log name with string if attempting to work with multiple log files
    If Not FileExists($sLogPath & $sLogName) Then ; if log file doesnt exist
        If Not _FileCreate($sLogPath & $sLogName) Then ; attempt to create it
            ConsoleWrite("! [ERROR] - Unable to create log file" & @CR)
            Exit(1) ; exit if unable to create log file
        EndIf
    EndIf
    Switch $sText ; test if text string contains keywords
        Case StringInStr($sText, "ERROR") > 0
            $sColor = "!" ; red
        Case StringInStr($sText, "WARN") > 0
            $sColor = "-" ; orange
        Case StringInStr($sText, "ACTION") > 0
            $sColor = "+" ; green
        Case Else
            $sColor = ">" ; blue
    EndSwitch
    If Not _FileWriteLog($sLogPath & $sLogName, $sText) Then ; attempt to write to log file
        ConsoleWrite("! [ERROR] - Unable to write to log file" & @CR)
        Exit(1) ; exit if unable to write to log file
    EndIf
    ConsoleWrite($sColor & " " & $sText & @CRLF) ; log to console and command window (if compiled)
EndFunc   ;==>_Log

Share this post


Link to post
Share on other sites

That's fine, but if you're writing to file a lot it might be worth rethinking.

You could also open all the log files you need, outside of function, and pass the handle in to function.

I actually AM writing to a log file a lot and although using file names instead of handles is more resource intensive I don't see a better way to do it. I don't want to store things in an array, and I don't want to have to declare things or do other actions outside the function.

I want it all self contained within the _Log function.

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

I don't understand. You're already passing a string into the function (albeit admittedly optional) that determines which logfile to write to... So it's already not all "self-contained" in the _Log function. What's the difference between that and just passing in a file handle? And what's your beef with arrays?  :ermm:

(Not trying to :argue: , just trying to understand the situation.)

One other tip: if you're writing a "logging framework" (big term, but ok), it's dangerous to determine the log level with keywords in the error message, for a number of reasons:

  • You can't easily rename a log level
  • You can't correctly log "> ACTION: I'm going to build a message to log as WARN or ERROR"
  • It's easy to mistype levels. Wouldn't want to miss "WRAN: Jack Bauer is coming for you, 24 seconds left to evacuate the building" :)

I advise having the log level as a separate non-optional parameter to the function and enforcing a certain fixed set of log levels.

Again, not trying to  :argue:, just a friendly advice from experience :)

Edited by SadBunny

Roses are FF0000, violets are 0000FF... All my base are belong to you.

Share this post


Link to post
Share on other sites

What about an array as suggested before but Local Static in the function? I too am a little confused as to how you think you can do when the suggestions you have been presented seem logical. If you don't want to store in an array, then why not? You didn't really make it clear why you're against this.


_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

@SadBunny, it is all self contained. By that I mean I don't want to have to add lines of code outside of the _Log function that make _Log() work or not. IE, variables, or fileopen's, filecloses, etc. I want every command that makes _Log work to be contained within the function.

however

I like your suggestions but unsure how best to implement. What do you purpose for the log level? A BitAND / BitOR ?

I would like to avoid having to search the string as well, but open to better ways to handle it.

@guinness, I'm not adamantly opposed to storing the handles in arrays, I just don't understand how I would pass the handle through to the function.

Share this post


Link to post
Share on other sites

#9 ·  Posted (edited)

Not understanding is one thing, dismissing it is another.

Edited by guinness

_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

Not understanding is one thing, dismissing it is another.

fair point. which is why I've posted code and asked for help.

I didn't 'dismiss' anything, simply said I don't want to.

Share this post


Link to post
Share on other sites

With your current attitude toward your coding seems a little backward "I don't want to store things in an array" etc..

You'll get along very slowly if you keep it.

If you store your file handles in an array inside the function there is no need to pass the handle in, that is just another option.

Here's some precedence of what I'd do, I'm not going to write it because that's what I think you're angling at.

You pass filename into function.

You check if that filename exists in your static array.

If it does, you use the file handle associated with it.

If it does not, you add the filename and associated handle to your static array and use it thereafter.


AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Share this post


Link to post
Share on other sites

#12 ·  Posted (edited)

With your current attitude toward your coding seems a little backward "I don't want to store things in an array" etc..

You'll get along very slowly if you keep it.

If you store your file handles in an array inside the function there is no need to pass the handle in, that is just another option.

Here's some precedence of what I'd do, I'm not going to write it because that's what I think you're angling at.

You pass filename into function.

You check if that filename exists in your static array.

If it does, you use the file handle associated with it.

If it does not, you add the filename and associated handle to your static array and use it thereafter.

If I wanted you to write all my code for me I wouldn't have posted my own code in the first place. (twice actually)

You've also seem to take a bit personally the fact I didn't want to go with your suggestion, even when you misinterpreted what I was asking. No, I'm not trying to 'keep track of them', im trying to create them and handle them within the log function on the fly.

Your series of steps makes no sense.

The entire point of what I'm trying to do is deal with a situation where I don't know the file names, I don't know how many log files you might be using in a given script, and I don't know what log level you want to log for any given log file.

I can't have 'static' arrays. Everything needs to be dynamic, able to handle you only ever calling 1 log file, or 10.

My biggest issue is I don't know how to create a variable based on a string.

If I could somehow dynamically make variables for however many different log files you might reference in the script I could assign a variable(handle) to each different log file that is referenced and I'd be fine.

Problem is I have no idea how to dynamically make variables, or if that's even the best way to go about it.

Edited by kor

Share this post


Link to post
Share on other sites

#13 ·  Posted (edited)

You are passing the filename of the log into your function, is that correct?

I assume so, cause you say it works.

When you add that filename to and array (Redim) you are essentially creating a variable dynamically, and storing an open file handle right next to it.

It does all the things you say it does not.

Edited by JohnOne

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

You are passing the filename of the log into your function, is that correct?

sort of. I'm passing a 'prefix' to a put at the beginning of a log file

IF and only if there are going to be multiple log files being spit out of the script. If there is only 1 log file then there is no need to add the prefix.

So the log function doesn't know the exact name of the file until it creates it (with or without the prefix)

 

EDIT: try this

#include <File.au3>

_Log("testing1", "HR")
_Log("testing2", "HR")
_Log("testing3", "Payroll")
_Log("testing4", "Payroll")
_Log("testing5")

Func _Log($sText, $sPrefix = "")
    Local $sColor, $sLogPath = @ScriptDir & "\logs\", $sLogName = @MON & "-" & @MDAY & "-" & @YEAR & "--" & @HOUR & "-" & @MIN & ".log"
    If $sPrefix <> "" Then $sLogName = $sPrefix & "-" & $sLogName ; prefix log name with string if attempting to work with multiple log files
    If Not FileExists($sLogPath & $sLogName) Then ; if log file doesnt exist
        If Not _FileCreate($sLogPath & $sLogName) Then ; attempt to create it
            ConsoleWrite("! [ERROR] - Unable to create log file" & @CR)
            Exit(1) ; exit if unable to create log file
        EndIf
    EndIf
    Switch $sText ; test if text string contains keywords
        Case StringInStr($sText, "ERROR") > 0
            $sColor = "!" ; red
        Case StringInStr($sText, "WARN") > 0
            $sColor = "-" ; orange
        Case StringInStr($sText, "ACTION") > 0
            $sColor = "+" ; green
        Case Else
            $sColor = ">" ; blue
    EndSwitch
    If Not _FileWriteLog($sLogPath & $sLogName, $sText) Then ; attempt to write to log file
        ConsoleWrite("! [ERROR] - Unable to write to log file" & @CR)
        Exit(1) ; exit if unable to write to log file
    EndIf
    ConsoleWrite($sColor & " " & $sText & @CRLF) ; log to console and command window (if compiled)
EndFunc   ;==>_Log
Edited by kor

Share this post


Link to post
Share on other sites

OK, I did, what about it, works just fine.

Right, when using filenames instead of handles.

I'm trying to accomplish the same thing but using file handles instead.

Share this post


Link to post
Share on other sites

I know exactly what you are trying to accomplish, get code without even trying what has been suggested.

Luckily, I'm tired of trying the nudge you and had a few minutes to spare, so here's some demo code.

#include <File.au3>

__Log("testing1", "HR")
__Log("testing2", "HR")
__Log("testing2", "HR")
__Log("testing3", "Payroll")
__Log("testing4", "Payroll")
__Log("testing5")

Func __Log($sText, $sPrefix = "")

    Local Static $array[1][2]
    Local $hfile = 0
    Local $filename = "nyFile.txt"
    If $sPrefix <> "" Then
        $filename = $sPrefix & "-" ^ $filename
    EndIf
    For $i = 0 To UBound($array) - 1
        If $array[$i][0] = $filename Then
            ConsoleWrite("File name and handle exists" & @LF)
            $hfile = $array[$i][1]
            ExitLoop
        EndIf
    Next
    If Not $hfile Then
        ConsoleWrite("File name not exists - adding" & @LF)
        $array[UBound($array) -1][0] = $filename
        $array[UBound($array) -1][1] = "get hande here"
        $hfile = $array[UBound($array) -1][1]
        ReDim $array[UBound($array) +1][2]
    EndIf

    ;Filewrite($hfile, "whatever")

EndFunc   ;==>__Log

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Share this post


Link to post
Share on other sites

#18 ·  Posted (edited)

 

I know exactly what you are trying to accomplish, get code without even trying what has been suggested.

Luckily, I'm tired of trying the nudge you and had a few minutes to spare, so here's some demo code.

#include <File.au3>

__Log("testing1", "HR")
__Log("testing2", "HR")
__Log("testing2", "HR")
__Log("testing3", "Payroll")
__Log("testing4", "Payroll")
__Log("testing5")

Func __Log($sText, $sPrefix = "")

    Local Static $array[1][2]
    Local $hfile = 0
    Local $filename = "nyFile.txt"
    If $sPrefix <> "" Then
        $filename = $sPrefix & "-" ^ $filename
    EndIf
    For $i = 0 To UBound($array) - 1
        If $array[$i][0] = $filename Then
            ConsoleWrite("File name and handle exists" & @LF)
            $hfile = $array[$i][1]
            ExitLoop
        EndIf
    Next
    If Not $hfile Then
        ConsoleWrite("File name not exists - adding" & @LF)
        $array[UBound($array) -1][0] = $filename
        $array[UBound($array) -1][1] = "get hande here"
        $hfile = $array[UBound($array) -1][1]
        ReDim $array[UBound($array) +1][2]
    EndIf

    ;Filewrite($hfile, "whatever")

EndFunc   ;==>__Log

 

Please stop accusing me of trying to get code for free. I'm trying to learn. If that is too much for you to handle then I'm not sure what to say.

I already have my own code which I've posted. So I certainly don't need free code.

Your code doesn't even work anyway. You have an error at line 16. It also continues to use file names rather than file handles which was the whole point of my thread.

I've got code that works, it not as efficient but it seems all you're interested in is being negative so I'll be on my way..

Edited by kor

Share this post


Link to post
Share on other sites

#20 ·  Posted (edited)

Please stop accusing me of trying to get code for free. I'm trying to learn. If that is too much for you to handle then I'm not sure what to say.

I already have my own code which I've posted. So I certainly don't need free code.

Your code doesn't even work anyway. You have an error at line 16. It also continues to use file names rather than file handles which was the whole point of my thread.

I've got code that works, it not as efficient but it seems all you're interested in is being negative so I'll be on my way..

 

1. The problem in that code is a typo. Change ^ to &. Fixed. No need to fight about. 

2. There seems to be a misunderstanding. File names and file handles are not mutually exclusive. You will still need a filename to create a filehandle for. After you have done that, you could technically forget the filename and only work against the handler. JohnOne just used both. Which may have complicated the example if you're not used to work with multidimensioned arrays, but it is actually quite useful. What JohnOne did is create an array where each element has two "fields", namely one for the filename and one for the handle created for that filename. Now you can do lots of things:

- Know both the filename and the filehandle for every file in a handy array

- Look up the filehandle by searching for the filename

- Easily loop through the files, handy for things like create a daily rollover system, close the files on exit, whatever you want

- Easily change the number of files/handles you're dealing with

... *gasp* ... anyway, lots of versatility o/

It is probably worth your time to actually try to get your head around what JohnOne suggested, because it is actually really useful for what you need ;)

3. About the log levels... This is from one of my personal libraries. I have adapted and simplified it for demo purposes but it has served me quite well.

Note: the minimum loglevel is now set to INFO, which means that it will not log TRACE or DEBUG. Set it to TRACE to log all.

 

#include <Date.au3>
#include <Array.au3>

Global $ALLOWED_LOGLEVELS[0]
initConfig()

; Convenient config variable on top of script to easily switch minimum loglevel to log.
; Set to Trace or Debug in dev/test phase and during go-live.
; If happy with functionality, set to Info level (2).
Global $CONFIG_MINIMUM_LOGLEVEL = _ArraySearch($ALLOWED_LOGLEVELS, "INFO")

; Note: you can also make the script read this from a config file every X minutes.
; Which is VERY USEFUL, as it allows you to change the minimum loglevel on the fly. Like, set it to Trace if it acts strange.

_myLogger("INFO", "Welcome to my script!")
_myLogger("DEBUG", "Starting a counter loop.")

For $i = 1 To 3
    _myLogger("TRACE", "Increasing counter, current value: " & $i)
Next

_myLogger("TRACE", "Something may be wrong, I may need to do some ERROR checking!")
_myLogger("ERROR", "My back itches but I can't reach it.")

_myLogger("TYPO", "This should generate an ERROR log about an unknown log level.")

Exit;


Func _myLogger($loglevel, $msg)

    Local $iUsedLevel = _ArraySearch($ALLOWED_LOGLEVELS, $loglevel)

    If $iUsedLevel == -1 Then
        ; prevent infinite recursion/stack overflow
        If _ArraySearch($ALLOWED_LOGLEVELS, "ERROR") == -1 Then
            MsgBox(16, "ERROR", "Fatal problem in logger, level ""ERROR"" is unknown.")
            Exit
        EndIf
        _myLogger("ERROR", "Kick developer for using an unknown loglevel: '" & $loglevel & "'. The message was: " & $msg)
        Return
    ElseIf $iUsedLevel < $CONFIG_MINIMUM_LOGLEVEL Then
        ; We are not logging this $msg, because its loglevel was below the configured threshold.
        Return
    EndIf

    $logline = _Now() & " | " & $loglevel & " | " & $msg & @CRLF

    ConsoleWrite($logline)

EndFunc   ;==>_myLogger

Func initConfig()
        ; NOTE: Make sure these loglevels are in INCREASING order of importance!
    Global $ALLOWED_LOGLEVELS[5]
    $ALLOWED_LOGLEVELS[0] = "TRACE"
    $ALLOWED_LOGLEVELS[1] = "DEBUG"
    $ALLOWED_LOGLEVELS[2] = "INFO"
    $ALLOWED_LOGLEVELS[3] = "WARN"
    $ALLOWED_LOGLEVELS[4] = "ERROR"
EndFunc   ;==>initConfig

Hope it's helpful. Good luck!

/edit: corrected typos and added infinite recursion safeguard.

Edited by SadBunny

Roses are FF0000, violets are 0000FF... All my base are belong to you.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now